MacOS SwiftUI中CanvasView无法触发DragGesture问题求助
问题:MacOS SwiftUI绘图应用Canvas无法触发DragGesture
我在MacOS平台使用SwiftUI开发绘图应用时遇到一个问题:Canvas内部点击无法触发DragGesture,但点击工具栏或右侧面板的元素时可以正常触发。已禁用所有其他视图(工具栏、右侧面板、标尺、标尺网格等),但问题仍然存在。
简化后的CanvasView代码
GeometryReader { geometry in Path { path in for curObj in dm.objects { DrawingHelper.draw( curObj, in: geometry.size) } } .stroke(lineWidth: 2) .foregroundColor(.black) .allowsHitTesting(true) .gesture( DragGesture(minimumDistance: 0) .onChanged { value in currentPoints.append(value.location) print( "in onChanged") } .onEnded { _ in print( "in onEnded") if currentPoints.count > 1 { let newObject = DrawingObject( tool: dm.curTool, points: currentPoints, ... ) dm.drawingObjects.append(newObject) } currentPoints.removeAll() } ) }
CanvasView调用代码
GeometryReader { geometry in HStack (spacing: 0) { // Remove space on right Rulers( dm ) { ZStack { // Draw rulerGrid on top CanvasView( dm ).background( .white ) if( dm.showRulerGrid == true ) { RulerGrid( dm ).background( .clear ) } } } onRulerDoubleClick: { isShowingRulerSettings.toggle( )} .popover(isPresented: $isShowingRulerSettings, arrowEdge: .trailing) { RulerSettingsView( dm ) } .background( .clear ) if( dm.showPanel ) { RightPanelView( dm ) .background( .black ) .animation(.easeInOut( duration: 0.5) ) // TODO: Update this .transition(.move(edge: .trailing)) } // End of Side Panel } .frame(maxWidth: .infinity, maxHeight: .infinity) .background( .clear ) }
解决方案
- 扩大Path的命中测试区域:默认情况下,
Path.stroke()只有线条本身能触发命中测试,线条较细时容易点击空。添加.contentShape(Rectangle())可以让整个Canvas区域都响应手势,或者用.fill(.clear)填充路径范围来扩大可点击区域。 - 禁用上层视图的手势拦截:即使
RulerGrid设置了.background(.clear),它默认仍会响应命中测试,需要给它加上.allowsHitTesting(false),避免挡住下层CanvasView的手势。 - 确保CanvasView占满可用空间:给CanvasView添加
.frame(maxWidth: .infinity, maxHeight: .infinity),保证它的布局范围和视觉范围一致,避免出现点击区域偏移。
修改后的代码示例
调整后的CanvasView
GeometryReader { geometry in Path { path in for curObj in dm.objects { DrawingHelper.draw( curObj, in: geometry.size) } } .stroke(lineWidth: 2) .foregroundColor(.black) // 让整个Canvas区域都能响应手势 .contentShape(Rectangle()) .allowsHitTesting(true) .gesture( DragGesture(minimumDistance: 0) .onChanged { value in currentPoints.append(value.location) print( "in onChanged") } .onEnded { _ in print( "in onEnded") if currentPoints.count > 1 { let newObject = DrawingObject( tool: dm.curTool, points: currentPoints // 补充其他必要参数 ) dm.drawingObjects.append(newObject) } currentPoints.removeAll() } ) // 确保占满父视图空间 .frame(maxWidth: .infinity, maxHeight: .infinity) }
调整后的调用代码
GeometryReader { geometry in HStack (spacing: 0) { Rulers( dm ) { ZStack { CanvasView( dm ) .background( .white ) .frame(maxWidth: .infinity, maxHeight: .infinity) if( dm.showRulerGrid == true ) { RulerGrid( dm ) .background( .clear ) // 禁止网格视图拦截手势 .allowsHitTesting(false) } } } onRulerDoubleClick: { isShowingRulerSettings.toggle( )} .popover(isPresented: $isShowingRulerSettings, arrowEdge: .trailing) { RulerSettingsView( dm ) } .background( .clear ) if( dm.showPanel ) { RightPanelView( dm ) .background( .black ) .animation(.easeInOut( duration: 0.5) ) .transition(.move(edge: .trailing)) } } .frame(maxWidth: .infinity, maxHeight: .infinity) .background( .clear ) }
内容的提问来源于stack exchange,提问作者Keith
相关产品推荐
相关产品推荐

