Search code examples
iosswiftuiios13zstack

Using SwiftUI to annotate an overlay with existing content shown beneath Path view


I have a SwiftUI app where some content is presented to the user, and then the user will annotate over it with a Path view.

The issue I have is that when I use a clear background for the drawing view, or no background at all, the content beneath it will appear as desired, but the gesture changes for the drawing action do not register.

If however, I assign a non-clear color for the background, then the gesture changes register and the drawing appears.

FWIW, I was able to achieve the desired effect using a UIKit based interface, and would just like to recreate it with SwiftUI now.

The simplified code below is a complete example that demonstrates my predicament. This is just an approximation of the code in use, so it's not exactly production quality.

By changing backgroundColor to clear or by commenting out the .background(self.backgroundColor) line, the BackgroundView will now appear, but onChanged does not get called, so no ink will appear.

If I run as is, then the ink will appear, but I cannot see the BackgroundView.

import SwiftUI

struct ContentView: View {
    var body: some View {
        GeometryReader { geometry in
            ZStack {
                DrawingView(backgroundColor: .white,
                            geometry: geometry)
                    .zIndex(1)

                BackgroundView()
            }
        }
    }
}

struct DrawingView: View {
    let backgroundColor: Color
    let geometry: GeometryProxy
    
    @State private var points = [CGPoint]()
    
    var body: some View {
        Path { path in
            guard !points.isEmpty else  { return }
            
            for index in 0..<points.count - 1 {
                path.move(to: points[index])
                path.addLine(to: points[index + 1])
            }
        }
        .stroke(Color.red, lineWidth: 3.0)
        .background(self.backgroundColor)
        .gesture(DragGesture(minimumDistance: 0.1)
        .onChanged({ changeValue in
            print("onChanged called")
            self.points.append(changeValue.location)
        }))
    }
}

// Just a simple View to fill the background
struct BackgroundView: View {
    var body: some View {
        Color.black
            .edgesIgnoringSafeArea(.all)
    }
}

Solution

  • Instead of background use .contentShape as

    .stroke(Color.red, lineWidth: 3.0)
    .contentShape(Rectangle())                    // << here !!
    .gesture(DragGesture(minimumDistance: 0.1)
    .onChanged({ changeValue in