Gesture Recognizer: What It Is, Touch Handling in iOS

Author: IT Sectr Published: 2026-02-27 Reading time: 8 min
Gesture Recognizer is an abstraction in UIKit that converts a sequence of user touches into a recognized gesture: tap, swipe, long press, pinch, or rotation. Instead of manually tracking coordinates in touchesBegan/touchesMoved methods, the developer adds a ready-made UIGestureRecognizer object to UIView and implements an action method. According to Apple Developer Documentation, UIGestureRecognizer supports 7 built-in subclasses covering 95% of typical gestures in mobile applications.

Key Takeaways

  • UIGestureRecognizer is an abstract UIKit class for recognizing touch patterns: taps, swipes, pinches, rotations, and long presses.
  • 7 built-in subclasses: UITapGestureRecognizer, UISwipeGestureRecognizer, UIPinchGestureRecognizer, UIRotationGestureRecognizer, UILongPressGestureRecognizer, UIPanGestureRecognizer, UIScreenEdgePanGestureRecognizer.
  • Each recognizer has a state (possible, began, changed, ended, cancelled, failed) that updates as fingers move.
  • To resolve conflicts between gestures, use the require(toFail:) method to set recognition priority.
  • Gesture Recognizer works with UIKit and UIViewRepresentable in SwiftUI, but native SwiftUI uses gesture modifiers.

What Is Gesture Recognizer?

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.

UIGestureRecognizer States

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.

Gesture Types in iOS

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.

  • UITapGestureRecognizer — recognizes single and multiple taps. Configurable number of taps (numberOfTapsRequired) and fingers (numberOfTouchesRequired). Used for buttons, links, element selection.
  • UISwipeGestureRecognizer — recognizes a swipe in one of four directions (right, left, up, down). The direction property sets the direction; numberOfTouchesRequired sets the number of fingers.
  • UIPanGestureRecognizer — continuous gesture for dragging. The translation(in:) method returns the offset from the starting point. Used for drag-and-drop, carousels, pull-to-refresh.
  • UIPinchGestureRecognizer — two-finger scaling. The scale property reflects the current scale factor. Detailed in the Pinch-to-Zoom article.
  • UIRotationGestureRecognizer — two-finger rotation. The rotation property stores the angle in radians. Used for rotating images, maps, canvases.
  • UILongPressGestureRecognizer — long press. Parameters: minimumPressDuration (seconds), allowableMovement (pixels). Used for context menus, element dragging.
  • UIScreenEdgePanGestureRecognizer — panning from the screen edge. The edges property specifies which edge to track. Used for navigation gestures, notification panels.

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.

Resolving Conflicts Between Gestures

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.

Swift Code Examples

Example 1: UITapGestureRecognizer — Tap on Image

Adds a single tap handler to UIImageView. On tap, the image changes opacity — a simple example showing gesture binding to a view.

swift
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.

Example 2: UISwipeGestureRecognizer — Swipe for Navigation

Implements a left swipe to go back to the previous screen. Demonstrates direction configuration and gesture binding to the root view of the controller.

swift
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.

Example 3: Gesture Combination with require(toFail:)

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.

swift
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

Can I use multiple Gesture Recognizers on one view?

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.

How is UIGestureRecognizer different from touchesBegan?

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.

Does Gesture Recognizer work in SwiftUI?

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

  • UIGestureRecognizer is a UIKit abstraction for decomposing gesture recognition logic into separate objects with automatic state management.
  • 7 built-in subclasses cover 95% of typical gestures: tap, swipe, pan, pinch, rotation, long press, and edge swipe.
  • Each recognizer goes through 7 states — from .possible to .ended/.failed, enabling precise tracking of discrete and continuous gestures.
  • The require(toFail:) method resolves conflicts between gestures on one view by setting recognition priority.
  • SwiftUI uses declarative gesture modifiers, but UIKit recognizers are available via UIViewRepresentable.
  • Gesture Recognizer is mandatory for UIKit projects; touchesBegan is only justified for custom drawing and low-level graphics.
  • Use require(toFail:) for tap/double-tap pairs and gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) for parallel gestures.

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.

Discuss the project

Read also