Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom UIGestureRecognizer for a two finger gesture

I have a custom UIGestureRecognizer for a two finger gesture that works perfectly except for it being very picky about how simultaneously the fingers have to touch the iOS-device for touchesBegan to be called with 2 touches. touchesBegan is often called with only one Touch even though I am trying to use two fingers.

Is there any way to make recognition for the number of Touches more forgiving in regards to how simultaneously you have to place your fingers on the touch screen?

I've noticed that a two finger tap is recognized even when you place first one finger and then another much later while still holding the first finger down.

Here is the code for my touchesBegan function:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {

      if touches.count != 2 {
         state = .failed
         return
      }

      // Capture the first touch and store some information about it.
      if trackedTouch == nil {
         trackedTouch = touches.min { $0.location(in: self.view?.window).x < $1.location(in: self.view?.window).x }
         strokePhase = .topSwipeStarted
         topSwipeStartPoint = (trackedTouch?.location(in: view?.window))!
         // Ignore the other touch that had a larger x-value
         for touch in touches {
            if touch != trackedTouch {
               ignore(touch, for: event)
            }
         }
      }
   }
like image 812
Melodius Avatar asked Feb 17 '18 13:02

Melodius


3 Answers

For two-finger gestures, touchesBegan is most likely going to be called twice: once you put the first finger on the screen, and once for the second one.

In the state you keep, you should keep track of both touches (or for that matter, all current touches), and only start the gesture once both touches have been received and the gesture's start condition has been met.

public class TwoFingerGestureRecognizer: UIGestureRecognizer {
    private var trackedTouches: Set<UITouch> = []

    public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        for touch in touches {
            if self.trackedTouches.count < 2 {
                self.trackedTouches.insert(touch)
            }
            else {
                self.ignore(touch, for: event)
            }
        }

        if self.trackedTouches.count == 2 {
            // put your current logic here
        }
    }

    public override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
    }

    public override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
        self.trackedTouches.subtract(touches)
    }

    public override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
        self.trackedTouches.subtract(touches)
    }

    public override func reset() {
        super.reset()

        self.trackedTouches = []
    }
}
like image 182
Christian Schnorr Avatar answered Oct 06 '22 21:10

Christian Schnorr


don't worry about touchesBegan:withEvent: instead use touchesEnded:withEvent: or touchesMoved:withEvent: if the end state does not contain both fingers, set it as .failed otherwise set it as .ended

tapping the screen with more than one finger simultaneously is impossible, so during touchesMoved:withEvent: you will find two fingers. I'm not sure about touchesEnded:withEvent: this one probably won't work since removing two fingers simultaneously is just as hard as applying two fingers simultaneously, but it's worth a try to see how it reacts.

like image 22
ɯɐɹʞ Avatar answered Oct 06 '22 21:10

ɯɐɹʞ


I'd recommend making your code a little more forgiving. Although touchesBegan/Moved/Ended/Cancelled respond to events of "one or more touches" (as stated in the Apple docs) relying on the precision of a user to simultaneously touch the screen with 2 fingers is not ideal. This is assuming you have multi-touch enabled, which it sounds like you do.

Try tracking the touches yourself and executing your logic when you're collection of touches reaches 2. Obviously you'll also have to track when your touch amount becomes more or less and handle things accordingly, but I'd guess you're already handling this if you're gesture is meant to be for 2 fingers only (aside from the extra logic you'd have to add in touchesBegan).

like image 28
Taylor2412 Avatar answered Oct 06 '22 21:10

Taylor2412