How do gesture recognisers interact with touches and with each other?

7.1.3 · L3 · Interview

Predict first

What are the defaults for a tap recogniser?

import UIKit

let g = UITapGestureRecognizer()
print(g.state.rawValue, g.numberOfTapsRequired, g.cancelsTouchesInView, g.delaysTouchesBegan)
  • 0 1 true false
  • 1 2 false true
  • 0 1 false false

Mental model. A gesture recogniser is a judge watching the touch. Until the judge rules, the other officials wait; once the judge rules, the touch is taken off their hands.

A gesture recogniser is attached to a view and receives that view’s touches before the view does. It runs a state machine: possible until it decides, then began, changed and ended for continuous gestures (pan, pinch), or straight to ended for discrete ones (tap, swipe), or failed / cancelled. When a recogniser recognises its gesture, by default it sends touchesCancelled to the view (cancelsTouchesInView is true), so a recognised tap stops the view’s own touch handling. delaysTouchesBegan (false by default) can hold touches back until it fails.

Conflicts are resolved by relationships. require(toFail:) makes one gesture wait for another (a single tap waits for a double tap to fail). The delegate’s gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) lets two run together (pinch and rotate). A scroll view’s pan recogniser competes with yours, which is why a horizontal swipe inside a vertical scroll view needs a delegate or gestureRecognizerShouldBegin that checks the direction. For controls (UIButton) UIKit has built-in rules so a tap recogniser on a superview does not steal the button’s touch.

A new recogniser is in the possible state (0), wants one tap, cancels the view’s touches when it recognises, and does not delay touches.

Common traps

  • Adding a tap recogniser to a view that contains buttons. Check the delegate or the recogniser may swallow touches meant for controls.
  • Forgetting cancelsTouchesInView. A recognised gesture cancels the view’s own touch handling unless you turn it off.

They might follow up with

  • How do you make a single and a double tap coexist? single.require(toFail: double), at the cost of a small delay for the single tap.
  • Are recognisers on a parent seen before the child’s? Recognisers on the hit-test view and its ancestors all receive the touch; the delegate decides who wins.

Related questions

  • 7.1.1 How does UIKit decide which view receives a touch, and why can a subview outside its parent’s bounds not be…
  • 7.1.2 What is the responder chain, and what is the first responder for?

← What is the responder chain, and what is the first responder for?