Swiftui longpress gesture onended and onchange not called on failure - is that right?

211 Views Asked by At

Encountering a problem in some hobby code in which a long-press appear to be misbehaving (that is, the previous action stopped happening ). I took the sample Apple code and started playing which revealed an anomaly (in my mind).

The anomaly is that if the gesture fails, then, whilst the underlying GestureState var is changed, neither the .onChange nor .onEnded modifiers are called. Similarly, .onEnded is ONLY invoked when the gesture succeeds. In a similar anomalous behavior, long-press, in isolation, appears to be detected and triggered, the instant the click happens. Which to me is contrary to the concept of a long-Press.

My sample code is below (modified Apple sample with print statements to track the sequence of events). If you click and hold the click until the duration expires then the associated actions occur as you expect, however, if you click, hold, and drag beyond the distance parameter or simply click and release, then the only place for detecting that failure is in the view that the gesture is attached to.

Does anyone know of a way of detecting a long-press failure?

import SwiftUI

struct ContentView: View {
  var body: some View {
    VStack {
      LongPressGestureView()
    }
    .padding()
  }
}

struct LongPressGestureView: View {
  @GestureState var isDetectingLongPress = false
  @State var completedLongPress = false
  
  @State var scaleFactor = 1.0
  
  
  var longPress: some Gesture {
    LongPressGesture(minimumDuration: 4, maximumDistance: 100)
      .updating($isDetectingLongPress) { currentState, gestureState,
        transaction in
        print("Gesture updating detected currentState: \(currentState)")
        print("Gesture updating: saw state of completedLongPress: \(completedLongPress)")
        gestureState = currentState
        transaction.animation = Animation.easeIn(duration: 2.0)
      }
      .onChanged() { state in
        print("Gesture onChange: saw state of isDetectingLongPress: \(state)")
        print("Gesture onChange: saw state of completedLongPress: \(completedLongPress)")
      }
     .onEnded { finished in
        print("Gesture onEnded detected completed: \(self.completedLongPress)")
        self.completedLongPress = finished
      }
  }
  
  var body: some View {
    Circle()
      .scale(scaleFactor)
      .fill(self.isDetectingLongPress ?
            Color.red :
              (self.completedLongPress ? Color.green : Color.blue))
      .frame(width: 100, height: 100, alignment: .center)
      .gesture(longPress)
      .onChange(of: self.isDetectingLongPress) {newState in
        print("View: onChange isDetectingLongPress: \(newState)")
        print("View: onChange isDetectingLongPress completedLongPress: \(completedLongPress)")
        scaleFactor = newState ? 3.0 : 1.0
      }
      .onChange(of: self.completedLongPress) { state in
        print("View: onChange completedLongPress isDetectingLongPress: \(isDetectingLongPress)")
        print("View: onChange completedLongPress: \(completedLongPress)")
        if (state) {
          scaleFactor = 1.0
          completedLongPress = false
        }
      }
      .animation(.easeInOut(duration: 1.0), value: scaleFactor)
  }
}
0

There are 0 best solutions below