SwiftUI -- Detect DragGesture cancellation when iPad dock is pulled up
Asked Answered
D

1

6

Here is a very simple example to show what I try to do. The red circle can be dragged around and when it is released it will go back to the original place, as shown in the first image. The problem occurs when iPad dock is pulled up in the middle of dragging and then the DragGesture's .onEnded will never be called and the red circle is stuck, as shown in the second image. I don't understand why .onEndedisn't called. What am I missing here?

I thought of a workaround by resetting offset in SceneDelegate's sceneDidBecomeActive, but it doesn't work if iPad dock is just pulled up but doesn't cause the app to become inactive, i.e. going to background mode. In this case, no functions in SceneDelegate will be called and I find it impossible to detect that DragGesture has been interrupted. Any possible solution?

struct ContentView: View {
  @State private var offset = CGSize.zero

  var body: some View {
    ZStack {
      Rectangle()
        .fill(Color.clear)
        .frame(width: 100, height: 100)
        .border(Color.black, width: 5)

      Circle()
        .fill(Color.red)
        .frame(width: 90, height: 90)
        .offset(offset)
        .gesture(
          DragGesture()
            .onChanged() { self.offset = $0.translation }
            .onEnded() { _ in self.offset = CGSize.zero })
    }
  }
}

Image 1

Image 2

Debrahdebrecen answered 16/2, 2020 at 21:43 Comment(0)
E
7

Try with GestureState as follows. It is temporary, by default, and when ended is reset to initial value by design.

struct ContentView: View {
  @GestureState private var offset = CGSize.zero

  var body: some View {
    ZStack {
      Rectangle()
        .fill(Color.clear)
        .frame(width: 100, height: 100)
        .border(Color.black, width: 5)

      Circle()
        .fill(Color.red)
        .frame(width: 90, height: 90)
        .offset(offset)
        .gesture(
          DragGesture()
            .updating($offset) { (value, gestureState, transaction) in
                gestureState = CGSize(width: value.location.x - value.startLocation.x, height: value.location.y - value.startLocation.y)
            })
    }
  }
}
Exordium answered 17/2, 2020 at 5:44 Comment(3)
wow, I think it does solve my problem. I've been stuck in this for one day. Thank you!Debrahdebrecen
This is also useful since .onEnded will not always trigger if another animation started during the execution of the DragGestureChantress
Absolutely wonderful. onEnded sometimes doesn't work and your solution solved that as well.Crossness

© 2022 - 2024 — McMap. All rights reserved.