Skip to content

Gesture Recognizers

Gesture recognizers in UIKit allow developers to detect and respond to user interactions such as taps, pinches, rotations, swipes, and long presses. By attaching UIGestureRecognizer subclasses to a UIView, apps can handle touch-based interactions efficiently. This document covers attaching tap, pinch, rotation, swipe, and long press gesture recognizers to a view.

Attaching Tap Gesture Recognizers to a View

A tap gesture recognizer (UITapGestureRecognizer) detects single or multiple taps on a view.

Implementation

Create and configure a UITapGestureRecognizer, then add it to a view.

Example

swift
import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var sampleView: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
        tapGesture.numberOfTapsRequired = 1 // Default is 1 (single tap)
        tapGesture.numberOfTouchesRequired = 1 // Default is 1
        sampleView.addGestureRecognizer(tapGesture)
    }
    
    @objc func handleTap(_ gesture: UITapGestureRecognizer) {
        print("Tapped at \(gesture.location(in: sampleView))")
    }
}

Key Points

  • Configuration: Set numberOfTapsRequired for double taps (2) or more, and numberOfTouchesRequired for multi-finger taps.
  • Target-Action: The target is the object handling the gesture, and action is the selector to call.
  • Location: Use gesture.location(in:) to get the tap’s coordinates in the view.

Attaching Pinch Gesture Recognizers to a View

A pinch gesture recognizer (UIPinchGestureRecognizer) detects two-finger pinch gestures for scaling.

Implementation

Add a UIPinchGestureRecognizer to a view and handle scaling in the action method.

Example

swift
override func viewDidLoad() {
    super.viewDidLoad()
    
    let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:)))
    sampleView.addGestureRecognizer(pinchGesture)
}

@objc func handlePinch(_ gesture: UIPinchGestureRecognizer) {
    guard gesture.view != nil else { return }
    if gesture.state == .began || gesture.state == .changed {
        let scale = gesture.scale
        gesture.view?.transform = gesture.view!.transform.scaledBy(x: scale, y: scale)
        gesture.scale = 1.0 // Reset scale to avoid compounding
        print("Pinched with scale: \(scale)")
    }
}

Key Points

  • Scale: gesture.scale indicates the pinch scale relative to the start of the gesture.
  • State: Handle .began or .changed states to apply transformations dynamically.
  • Reset Scale: Set gesture.scale = 1.0 after applying the transform to prevent cumulative scaling.

Attaching a Rotation Gesture Recognizer to a View

A rotation gesture recognizer (UIRotationGestureRecognizer) detects two-finger rotation gestures.

Implementation

Add a UIRotationGestureRecognizer and apply a rotation transform.

Example

swift
override func viewDidLoad() {
    super.viewDidLoad()
    
    let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(handleRotation(_:)))
    sampleView.addGestureRecognizer(rotationGesture)
}

@objc func handleRotation(_ gesture: UIRotationGestureRecognizer) {
    guard gesture.view != nil else { return }
    if gesture.state == .began || gesture.state == .changed {
        let rotation = gesture.rotation
        gesture.view?.transform = gesture.view!.transform.rotated(by: rotation)
        gesture.rotation = 0.0 // Reset rotation to avoid compounding
        print("Rotated by: \(rotation) radians")
    }
}

Key Points

  • Rotation: gesture.rotation provides the rotation angle in radians.
  • State: Apply transformations in .began or .changed states.
  • Reset Rotation: Set gesture.rotation = 0.0 after applying the transform to prevent cumulative rotation.

Attaching Swipe Gesture Recognizers to a View

A swipe gesture recognizer (UISwipeGestureRecognizer) detects swipes in specified directions.

Implementation

Configure a UISwipeGestureRecognizer with a direction and add it to a view.

Example

swift
override func viewDidLoad() {
    super.viewDidLoad()
    
    let swipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(_:)))
    swipeGesture.direction = .right // Can combine directions: [.right, .left]
    sampleView.addGestureRecognizer(swipeGesture)
}

@objc func handleSwipe(_ gesture: UISwipeGestureRecognizer) {
    let direction = gesture.direction
    print("Swiped \(direction == .right ? "right" : "other")")
}

Key Points

  • Direction: Set direction to .right, .left, .up, or .down (or combine with |). Default is .right.
  • Single Event: Swipes are discrete gestures, triggering only when completed.
  • Multiple Recognizers: Use multiple UISwipeGestureRecognizer instances for different directions.

Attaching a Long Press Gesture Recognizer to a View

A long press gesture recognizer (UILongPressGestureRecognizer) detects presses held for a specified duration.

Implementation

Add a UILongPressGestureRecognizer and configure properties like duration or allowable movement.

Example

swift
override func viewDidLoad() {
    super.viewDidLoad()
    
    let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))
    longPressGesture.minimumPressDuration = 0.5 // Seconds
    longPressGesture.allowableMovement = 10 // Pixels
    sampleView.addGestureRecognizer(longPressGesture)
}

@objc func handleLongPress(_ gesture: UILongPressGestureRecognizer) {
    if gesture.state == .began {
        print("Long press began at \(gesture.location(in: sampleView))")
    } else if gesture.state == .ended {
        print("Long press ended")
    }
}

Key Points

  • Duration: Set minimumPressDuration (default is 0.5 seconds).
  • Movement: allowableMovement defines how far the finger can move before the gesture fails (default is 10 pixels).
  • State: Handle .began, .changed, or .ended states for continuous feedback.

Gesture Recognizer Management

Simultaneous Recognition

Allow multiple gestures to work together:

swift
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true // Enable simultaneous pinch and rotation, for example
}

Set the delegate and implement UIGestureRecognizerDelegate:

swift
pinchGesture.delegate = self
rotationGesture.delegate = self

Requiring Gesture Failure

Make one gesture depend on another failing:

swift
tapGesture.require(toFail: longPressGesture) // Tap only triggers if long press fails

Best Practices

  • Enable User Interaction: Ensure view.isUserInteractionEnabled = true.
  • Minimal Gestures: Use only necessary recognizers to avoid conflicts.
  • Delegate: Use UIGestureRecognizerDelegate to manage conflicts or simultaneous gestures.
  • Accessibility: Ensure gestures are optional or provide alternative controls for VoiceOver users.
  • Testing: Test on various devices (iPhone, iPad) and input methods (touch, trackpad).
  • State Handling: Check gesture.state to respond appropriately to gesture phases.

Limitations

  • Conflicts: Multiple gestures on the same view can conflict; use delegate methods or require(toFail:) to resolve.
  • Precision: Some gestures (e.g., pinch, rotation) require multi-touch, which may not work with all input devices.
  • iOS Only: Gesture recognizers are UIKit-specific and unavailable in macOS without Mac Catalyst.

Summary

UIKit gesture recognizers (UITapGestureRecognizer, UIPinchGestureRecognizer, UIRotationGestureRecognizer, UISwipeGestureRecognizer, UILongPressGestureRecognizer) enable rich touch interactions in iOS apps. By configuring properties, handling states, and managing conflicts, developers can create intuitive and responsive user experiences tailored to specific interactions.

Released under the MIT License.