How do I correctly collect the click, drag and release of a mous

ghz 8months ago ⋅ 162 views

How do I correctly collect the click, drag and release of a mouse event for CanvasView in SwiftUI on MacOS

For this drawing application on MacOS using SwiftUI and I'm having an interesting issue. The DragGesture does not trigger when I click inside my Canvas. It does trigger if I click on an item in my toolbar, or right panel. I have disabled every single view (Toolbars, Right Panel, Rulers, RulerGrid etc) and the problem still persists.

Simplified CanvasView looks like this:

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()
        }
    )

Edit-1 - How the CanvasView is being called

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 )
}

Answers

It seems like the issue might be related to the layers of views in your SwiftUI hierarchy. Since the DragGesture is not triggering when clicking inside the CanvasView, it's possible that another view is intercepting the touch events before they reach the CanvasView.

Here are a few things you can try to troubleshoot and potentially fix the issue:

  1. Change Z-Index: Ensure that the CanvasView is placed at the top of the Z-index in your view hierarchy. You can achieve this by reordering the views inside the HStack. Move the CanvasView to the last position within the ZStack.

  2. Check for Overlapping Views: Make sure that there are no invisible or overlapping views above the CanvasView that might be intercepting the touch events.

  3. Hit Testing: Set allowsHitTesting(true) for all the parent views of CanvasView, including the HStack and the outermost GeometryReader.

  4. View Debugging: Use Xcode's View Debugger to inspect the view hierarchy during runtime. This can help you visualize the layout and identify any issues with view positioning or overlapping.

  5. Explicit Frame: Provide an explicit frame to the CanvasView to ensure that it occupies the entire available space within its parent container. Sometimes, views may not receive touch events if their frame is not correctly defined.

  6. Remove Gesture Conflicts: Ensure that there are no conflicting gestures or event handlers defined in other parts of your code that might interfere with the DragGesture.

By applying these troubleshooting steps, you should be able to identify and resolve the issue with the DragGesture not triggering within the CanvasView.