Fix tap gesture blocked by line brush drag gesture

The DragGesture with minimumDistance: 0 for line brush mode was
consuming all touches, preventing tap detection for Object/Person/Wire
tools. Fixed by:

1. Only attaching line brush gesture when in line brush mode
2. Added conditional .if() view modifier extension
3. Added debug logging to tap gesture to track gesture flow

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-24 13:05:30 -05:00
parent 52094862e0
commit 316db3e1eb

View File

@@ -8,6 +8,19 @@
import SwiftUI import SwiftUI
import UIKit import UIKit
// MARK: - Conditional View Modifier
extension View {
@ViewBuilder
func `if`<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View {
if condition {
transform(self)
} else {
self
}
}
}
struct CanvasView: View { struct CanvasView: View {
@Bindable var viewModel: EditorViewModel @Bindable var viewModel: EditorViewModel
@@ -67,11 +80,14 @@ struct CanvasView: View {
} }
} }
.contentShape(Rectangle()) .contentShape(Rectangle())
.gesture(lineBrushGesture(in: geometry))
.gesture(tapGesture(in: geometry)) .gesture(tapGesture(in: geometry))
.gesture(magnificationGesture(in: geometry)) .gesture(magnificationGesture(in: geometry))
.simultaneousGesture(dragGesture(in: geometry)) .simultaneousGesture(dragGesture(in: geometry))
.simultaneousGesture(longPressGesture) .simultaneousGesture(longPressGesture)
// Only attach line brush gesture when in line brush mode
.if(viewModel.selectedTool == .wire && viewModel.isLineBrushMode) { view in
view.gesture(lineBrushGesture(in: geometry))
}
.onTapGesture(count: 2) { .onTapGesture(count: 2) {
doubleTapZoom() doubleTapZoom()
} }
@@ -127,16 +143,30 @@ struct CanvasView: View {
private func tapGesture(in geometry: GeometryProxy) -> some Gesture { private func tapGesture(in geometry: GeometryProxy) -> some Gesture {
SpatialTapGesture() SpatialTapGesture()
.onEnded { value in .onEnded { value in
guard !viewModel.isProcessing, DebugLogger.action("CanvasView tap at \(value.location)")
!viewModel.showingMaskConfirmation, DebugLogger.state("isProcessing: \(viewModel.isProcessing), showingMaskConfirmation: \(viewModel.showingMaskConfirmation), tool: \(viewModel.selectedTool)")
viewModel.selectedTool != .brush else { return }
guard !viewModel.isProcessing else {
DebugLogger.log("Tap ignored - isProcessing")
return
}
guard !viewModel.showingMaskConfirmation else {
DebugLogger.log("Tap ignored - showingMaskConfirmation")
return
}
guard viewModel.selectedTool != .brush else {
DebugLogger.log("Tap ignored - brush tool selected")
return
}
// Skip tap if in line brush mode // Skip tap if in line brush mode
if viewModel.selectedTool == .wire && viewModel.isLineBrushMode { if viewModel.selectedTool == .wire && viewModel.isLineBrushMode {
DebugLogger.log("Tap ignored - line brush mode")
return return
} }
let imagePoint = convertViewPointToImagePoint(value.location, in: geometry.size) let imagePoint = convertViewPointToImagePoint(value.location, in: geometry.size)
DebugLogger.log("Converted to image point: \(imagePoint)")
Task { Task {
await viewModel.handleTap(at: imagePoint) await viewModel.handleTap(at: imagePoint)
} }
@@ -146,9 +176,11 @@ struct CanvasView: View {
private func lineBrushGesture(in geometry: GeometryProxy) -> some Gesture { private func lineBrushGesture(in geometry: GeometryProxy) -> some Gesture {
DragGesture(minimumDistance: 0) DragGesture(minimumDistance: 0)
.onChanged { value in .onChanged { value in
// Only activate for wire tool in line brush mode
guard viewModel.selectedTool == .wire, guard viewModel.selectedTool == .wire,
viewModel.isLineBrushMode, viewModel.isLineBrushMode else { return }
!viewModel.isProcessing,
guard !viewModel.isProcessing,
!viewModel.showingMaskConfirmation else { return } !viewModel.showingMaskConfirmation else { return }
let imagePoint = convertViewPointToImagePoint(value.location, in: geometry.size) let imagePoint = convertViewPointToImagePoint(value.location, in: geometry.size)