swiftuixcode11.3

How to stop SwiftUI DragGesture from dying when View content changes


In my app, I drag a View horizontally to set a position of a model object. I can also drag it downward and release it to delete the model object. When it has been dragged downward far enough, I indicate the potential for deletion by changing its appearance. The problem is that this change interrupts the DragGesture. I don't understand why this happens.

The example code below demonstrates the problem. You can drag the light blue box side to side. If you pull down, it and it turns to the "rays" system image, but the drag dies.

The DragGesture is applied to the ZStack with a size of 50x50. The ZStack should continue to exist across that state change, no? Why is the drag gesture dying?

struct ContentView: View {
    var body: some View {
        ZStack {
            DraggableThing()
        }.frame(width: 300, height: 300)
         .border(Color.black, width: 1)
    }
}

struct DraggableThing: View {

    @State private var willDeleteIfDropped = false
    @State private var xPosition: CGFloat = 150

    var body: some View {
        //Rectangle()
        //    .fill(willDeleteIfDropped ? Color.red : Color.blue.opacity(0.3))
        ZStack {
            if willDeleteIfDropped {
                Image(systemName: "rays")
            } else {
                Rectangle().fill(Color.blue.opacity(0.3))
            }
        }
            .frame(width: 50, height: 50)
            .position(x: xPosition, y: 150)
            .gesture(DragGesture()
                .onChanged { val in
                    print("drag changed \(val.translation)")
                    self.xPosition = 150 + val.translation.width
                    self.willDeleteIfDropped = (val.translation.height > 25)
                }
                .onEnded { val in
                    print("drag ended")
                    self.xPosition = 150
                }
            )
    }
}

Solution

  • You need to keep content, which originally captured gesture. So your goal can be achieved with the following changes:

    ZStack {
        Rectangle().fill(Color.blue.opacity(willDeleteIfDropped ? 0.0 : 0.3))
        if willDeleteIfDropped {
            Image(systemName: "rays")
        }
    }