From 316db3e1eb582b933ce166daff07733a6db1d30c Mon Sep 17 00:00:00 2001 From: jared Date: Sat, 24 Jan 2026 13:05:30 -0500 Subject: [PATCH] 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 --- CheapRetouch/Features/Editor/CanvasView.swift | 44 ++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/CheapRetouch/Features/Editor/CanvasView.swift b/CheapRetouch/Features/Editor/CanvasView.swift index 29402c6..89c3f9d 100644 --- a/CheapRetouch/Features/Editor/CanvasView.swift +++ b/CheapRetouch/Features/Editor/CanvasView.swift @@ -8,6 +8,19 @@ import SwiftUI import UIKit +// MARK: - Conditional View Modifier + +extension View { + @ViewBuilder + func `if`(_ condition: Bool, transform: (Self) -> Content) -> some View { + if condition { + transform(self) + } else { + self + } + } +} + struct CanvasView: View { @Bindable var viewModel: EditorViewModel @@ -67,11 +80,14 @@ struct CanvasView: View { } } .contentShape(Rectangle()) - .gesture(lineBrushGesture(in: geometry)) .gesture(tapGesture(in: geometry)) .gesture(magnificationGesture(in: geometry)) .simultaneousGesture(dragGesture(in: geometry)) .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) { doubleTapZoom() } @@ -127,16 +143,30 @@ struct CanvasView: View { private func tapGesture(in geometry: GeometryProxy) -> some Gesture { SpatialTapGesture() .onEnded { value in - guard !viewModel.isProcessing, - !viewModel.showingMaskConfirmation, - viewModel.selectedTool != .brush else { return } + DebugLogger.action("CanvasView tap at \(value.location)") + DebugLogger.state("isProcessing: \(viewModel.isProcessing), showingMaskConfirmation: \(viewModel.showingMaskConfirmation), tool: \(viewModel.selectedTool)") + + 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 if viewModel.selectedTool == .wire && viewModel.isLineBrushMode { + DebugLogger.log("Tap ignored - line brush mode") return } let imagePoint = convertViewPointToImagePoint(value.location, in: geometry.size) + DebugLogger.log("Converted to image point: \(imagePoint)") Task { await viewModel.handleTap(at: imagePoint) } @@ -146,9 +176,11 @@ struct CanvasView: View { private func lineBrushGesture(in geometry: GeometryProxy) -> some Gesture { DragGesture(minimumDistance: 0) .onChanged { value in + // Only activate for wire tool in line brush mode guard viewModel.selectedTool == .wire, - viewModel.isLineBrushMode, - !viewModel.isProcessing, + viewModel.isLineBrushMode else { return } + + guard !viewModel.isProcessing, !viewModel.showingMaskConfirmation else { return } let imagePoint = convertViewPointToImagePoint(value.location, in: geometry.size)