Key Takeaways
UIGestureRecognizer is an abstract base class in UIKit that decomposes the gesture recognition process into separate objects. Each subclass handles one gesture type: UITapGestureRecognizer handles a tap with a specific number of touches, UISwipeGestureRecognizer handles a swipe in a given direction. The developer adds the recognizer to UIView via the addGestureRecognizer(:) method, and UIKit automatically tracks touches, updates state, and calls an action upon recognition.
Before UIGestureRecognizer (iOS 3.2, 2010), developers overrode UIResponder methods — touchesBegan, touchesMoved, touchesEnded — and manually analyzed touch trajectories. This led to code duplication and errors when handling simultaneous touches. Apple encapsulated this logic in UIGestureRecognizer, adding support for multitouch, gesture cancellation, and simultaneous operation of multiple recognizers on one view.
According to WWDC sessions, Gesture Recognizer handles up to 11 simultaneous touches on iPad and 5 on iPhone. At IT Sectr, we use UIGestureRecognizer as the standard way to handle user input in all UIKit projects — this eliminated bugs associated with manual touchesBegan tracking.
Each UIGestureRecognizer goes through 7 possible states, defined in the UIGestureRecognizer.State enum. These states reflect the recognition lifecycle: from touch detection to completion or cancellation. Understanding states is critical for implementing custom recognizers and debugging conflicts.
| State | Meaning | When It Occurs |
|---|---|---|
| .possible | Initial state, gesture not yet recognized | Immediately after being added to a view |
| .began | Gesture recognized and started executing | On first finger movement for pan/longPress |
| .changed | Gesture parameters changed (coordinates, angle) | On each finger movement |
| .ended | User lifted finger, gesture completed | On touchesEnded |
| .cancelled | Gesture interrupted by system (incoming call, orientation change) | On touchesCancelled |
| .failed | Gesture not recognized based on conditions | On touchesCancelled without recognition |
| .recognized | Synonym for .ended; gesture successfully recognized | Same as .ended |
Discrete gestures (tap, swipe) transition from .possible directly to .ended or .failed. Continuous gestures (pan, pinch, rotation, longPress) go through .possible → .began → .changed (repeatedly) → .ended. In the action method, check gestureRecognizer.state — this lets you distinguish the beginning, change, and end of a gesture.
UIKit provides 7 built-in UIGestureRecognizer subclasses, covering most interaction scenarios. Each subclass has specific settings: numberOfTapsRequired for tap, direction for swipe, minimumPressDuration for long press.
For custom gestures (e.g., drawing a zigzag), create a UIGestureRecognizer subclass overriding touchesBegan, touchesMoved, touchesEnded and updating the state. Apple recommends using built-in classes where possible — they are optimized and interact correctly with each other.
When multiple UIGestureRecognizer instances are used on one UIView (e.g., tap and double-tap), a recognition conflict occurs: on a double tap, the single tap fires first. Apple provides the require(toFail:) method to delay one gesture's recognition until another fails.
The mechanism works like this: by calling tapRecognizer.require(toFail: doubleTapRecognizer), you specify that tapRecognizer will transition to .recognized state only after doubleTapRecognizer finishes in .failed. This adds a ~0.3 second delay before executing the single tap — the user taps twice, and the first tap is ignored. An alternative approach is the UIGestureRecognizerDelegate with the gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) method, which allows simultaneous recognition (e.g., pan + pinch for a map).
According to WWDC 2020, about 15% of bugs in UIKit applications are related to improper gesture conflict configuration. At IT Sectr, we standardized the approach: each screen has a gesture scheme with require(toFail:) priorities — this completely eliminated double-tap firing bugs.
Adds a single tap handler to UIImageView. On tap, the image changes opacity — a simple example showing gesture binding to a view.
import UIKit
class ImageViewController: UIViewController {
@IBOutlet private var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(
target: self,
action: #selector(handleTap(_:))
)
tap.numberOfTapsRequired = 1
imageView.addGestureRecognizer(tap)
imageView.isUserInteractionEnabled = true
}
@objc private func handleTap(_: UITapGestureRecognizer) {
UIView.animate(withDuration: 0.2) {
self.imageView.alpha = self.imageView.alpha == 1.0 ? 0.5 : 1.0
}
}
}
Key point: isUserInteractionEnabled on UIImageView defaults to false — without this flag, the Gesture Recognizer won't receive touches. For UIView and UIButton, this flag is enabled by default. The action method accepts a UITapGestureRecognizer parameter, through which you can get location(in:) to determine tap coordinates.
Implements a left swipe to go back to the previous screen. Demonstrates direction configuration and gesture binding to the root view of the controller.
import UIKit
class DetailViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let swipeLeft = UISwipeGestureRecognizer(
target: self,
action: #selector(handleSwipe(_:))
)
swipeLeft.direction = .left
view.addGestureRecognizer(swipeLeft)
}
@objc private func handleSwipe(_: UISwipeGestureRecognizer) {
navigationController?.popViewController(animated: true)
}
}
UISwipeGestureRecognizer is a discrete gesture: it transitions to .recognized immediately after recognition, without intermediate .changed states. Therefore, you don't need to check state in the action — the gesture either is recognized (action fires) or not. The direction property accepts one of four values: .left, .right, .up, .down. To support multiple directions, create separate recognizers for each.
Shows how to configure single and double tap on one view without conflict. The double-tap recognizer has priority — the single tap fires only if the double tap is not recognized.
import UIKit
class TapViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let singleTap = UITapGestureRecognizer(
target: self,
action: #selector(handleSingleTap)
)
singleTap.numberOfTapsRequired = 1
let doubleTap = UITapGestureRecognizer(
target: self,
action: #selector(handleDoubleTap)
)
doubleTap.numberOfTapsRequired = 2
singleTap.require(toFail: doubleTap)
view.addGestureRecognizer(singleTap)
view.addGestureRecognizer(doubleTap)
}
@objc private func handleSingleTap() {
print("Single tap — after 0.3s delay")
}
@objc private func handleDoubleTap() {
print("Double tap — instant")
}
}
Without require(toFail:), on a double tap, handleSingleTap fires first, then handleDoubleTap — this breaks the UX. With require(toFail:), the single tap waits ~0.3 seconds to ensure a second tap does not follow. At IT Sectr, this pattern is used in image editors and galleries, where a double tap zooms and a single tap selects an element.
Frequently Asked Questions
Yes, UIView supports multiple UIGestureRecognizer instances simultaneously. To resolve conflicts, use the require(toFail:) method to set recognition order. For parallel gesture operation (e.g., pan + pinch on a map), implement the gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) delegate method returning true.
UIGestureRecognizer is a high-level abstraction that automatically recognizes touch patterns and manages states. touchesBegan is a low-level UIResponder method requiring manual coordinate tracking, timing, and touch cancellation. Gesture Recognizer is simpler, more reliable, and preferred for standard gestures; touchesBegan is only justified for custom graphics.
Native SwiftUI uses modifiers: onTapGesture, onLongPressGesture, DragGesture, MagnificationGesture, RotationGesture. These are declarative analogs of UIGestureRecognizer integrated into the SwiftUI hierarchy. If needed, you can wrap a UIKit recognizer via UIViewRepresentable, but Apple recommends using native SwiftUI gestures.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also