iosswiftuigesturerecognizeruitapgesturerecognizeruilongpressgesturerecogni

Resolve UITapGestureRecognizer and UILongPressGestureRecognizer simultaneously and fire UIGestureRecognizer.State.began on finger touch down


First of all, none of the already answered questions didn't help me

  1. Swift: Long Press Gesture Recognizer - Detect taps and Long Press
  2. Using tap gesture and long press at the same time in Table View
  3. Long Press Gesture Recognizer Only Fired When Finger is Lifted
  4. And so on

The code working almost fine except one thing: the long press gesture only called when I lift my finger up from the screen. But I need to get behaviour like in Instagram Stories (when you can switch between stories and hold your finger to pause pause some story).

My question is more about how to force UILongPressGesture to fire when user touch finger down, but not up.

Here's my code:

private func setupTapGestures() {
    tapRecognizer = UITapGestureRecognizer()
    tapRecognizer?.addTarget(self, action: #selector(handleTapGesture(_:)))
    tapRecognizer?.delegate = self
    view.addGestureRecognizer(tapRecognizer!)

    longPressRecognizer = UILongPressGestureRecognizer()
    longPressRecognizer?.addTarget(self, action: #selector(handleLongPressGesture(_:)))
    longPressRecognizer?.minimumPressDuration = 0.1
    longPressRecognizer?.delegate = self
    view.addGestureRecognizer(longPressRecognizer!)
}

@objc func handleTapGesture(_ gestureRecognizer: UIGestureRecognizer) {
    let width = view.frame.width
    let point = gestureRecognizer.location(in: view)
    viewModel?.tapAction(viewWidth: width, tapPoint: point)
    Swift.print("Tap gesture")
}

@objc func handleLongPressGesture(_ gestureRecognizer: UILongPressGestureRecognizer) {
    if gestureRecognizer.state == .began {
        Swift.print("Began")
    } else if gestureRecognizer.state == .ended {
        Swift.print("Ended")
    }
}

UIGestureRecognizerDelegate:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    // Don't recognize a single tap until a long-press fails
    if gestureRecognizer == tapRecognizer && otherGestureRecognizer == longPressRecognizer {
        return true
    }
    return false
}

shouldRequireFailureOf docs

Any suggestions or ideas?


Solution

  • I wonder if your implementation of shouldRequireFailureOf is causing an issue?

    This works just fine for me (note: I used .minimumPressDuration = 0.25 because it's a little difficult to tap in under 0.1 seconds):

    class GestureTestViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            setupTapGestures()
        }
    
        private func setupTapGestures() -> Void {
    
            let singleTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(_:)))
            view.addGestureRecognizer(singleTapGesture)
    
            let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPressGesture(_:)))
            longPressGesture.minimumPressDuration = 0.25
            view.addGestureRecognizer(longPressGesture)
    
        }
    
        @objc func handleLongPressGesture(_ gesture: UILongPressGestureRecognizer) -> Void {
            if gesture.state == .began {
                print("Did Long Press (began)")
            }
            if gesture.state == .ended {
                print("Did Long Press (ended)")
            }
        }
    
        @objc func handleTapGesture(_ gesture: UITapGestureRecognizer) -> Void {
            print("Did Single Tap")
        }
    
    }
    

    When I tap, I get "Did Single Tap" in the debug console.

    When I tap and hold, I quickly get "Did Long Press (began)", and on finger-lift I get "Did Long Press (ended)"