Skip to main content

Handling User Input and Gestures

1. Using UITapGestureRecognizer

Property Value Description
numberOfTapsRequired 1 Number of taps required to trigger the gesture
numberOfTouchesRequired 1 Number of fingers required to trigger the gesture
cancelsTouchesInView true Whether to cancel touches in the view when gesture is recognized

Example: Basic Tap Gesture Setup

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap))
        view.addGestureRecognizer(tapGesture)
    }
    
    @objc func handleTap(sender: UITapGestureRecognizer) {
        print("View tapped at \(sender.location(in: view))")
    }
}

Example: Multi-Tap Gesture

let doubleTap = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap))
doubleTap.numberOfTapsRequired = 2
view.addGestureRecognizer(doubleTap)

@objc func handleDoubleTap(sender: UITapGestureRecognizer) {
    print("Double tap recognized")
}
Note: UITapGestureRecognizer is part of UIKit and requires a UIView to be added to the view hierarchy.

2. Using UISwipeGestureRecognizer

Property Value Description
direction .left, .right, .up, .down Specifies the swipe direction to recognize
numberOfTouchesRequired 1, 2, etc. Number of fingers required to trigger the swipe
numberOfTouchesRequired 1, 2, etc. Number of fingers required to trigger the swipe

Example: Basic Swipe Gesture

let swipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe))
swipeGesture.direction = .right
view.addGestureRecognizer(swipeGesture)

@objc func handleSwipe(_ gesture: UISwipeGestureRecognizer) {
    print("Swiped right")
}

Example: Multi-Finger Swipe

let multiSwipe = UISwipeGestureRecognizer(target: self, action: #selector(handleMultiSwipe))
multiSwipe.direction = .up
multiSwipe.numberOfTouchesRequired = 2
view.addGestureRecognizer(multiSwipe)

@objc func handleMultiSwipe(_ gesture: UISwipeGestureRecognizer) {
    print("Two-finger swipe up")
}

3. Using UIPanGestureRecognizer

Property Value Description
delegate AnyObject? Delegate object that receives pan gesture events
minimumNumberOfTouches Int Minimum number of touches required to recognize the gesture (default: 1)
maximumNumberOfTouches Int Maximum number of touches to recognize the gesture (default: Int.max)
translationInView CGPoint Current translation of the gesture in the specified view
velocityInView CGVector Velocity of the gesture in the specified view

Example: Basic Pan Gesture Setup

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var panView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
        panView.addGestureRecognizer(panGesture)
    }

    @objc func handlePanGesture(_ gesture: UIPanGestureRecognizer) {
        let translation = gesture.translation(in: view)
        panView.center.x += translation.x
        panView.center.y += translation.y
        gesture.setTranslation(CGPoint.zero, in: view)
    }
}

Example: Pan Gesture with Velocity

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var panView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
        panView.addGestureRecognizer(panGesture)
    }

    @objc func handlePanGesture(_ gesture: UIPanGestureRecognizer) {
        let velocity = gesture.velocity(in: view)
        let translation = gesture.translation(in: view)

        switch gesture.state {
        case .began:
            print("Pan began")
        case .changed:
            panView.center.x += translation.x
            panView.center.y += translation.y
            gesture.setTranslation(CGPoint.zero, in: view)
        case .ended:
            print("Pan ended with velocity: \\(velocity)")
        default:
            break
        }
    }
}
Note: The translationInView property accumulates the total translation since the gesture began. It's important to reset it to zero after applying the translation to prevent cumulative movement.
Warning: Always set the translation to zero after applying it to avoid unexpected behavior when the gesture continues.

4. Using UIPinchGestureRecognizer

Property Value Description
minimumScale 0.01 Minimum scale factor allowed for the pinch gesture.
maximumScale 10.0 Maximum scale factor allowed for the pinch gesture.
scale 1.0 Current scale factor of the pinch gesture.

Example: Setting up a UIPinchGestureRecognizer

class ViewController: UIViewController {
    @IBOutlet weak var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch))
        imageView.addGestureRecognizer(pinchGesture)
        imageView.isUserInteractionEnabled = true
    }

    @objc func handlePinch(_ gesture: UIPinchGestureRecognizer) {
        imageView.transform = imageView.transform.scaledBy(x: gesture.scale, y: gesture.scale)
        gesture.scale = 1.0
    }
}
Note: The scale property of UIPinchGestureRecognizer is reset to 1.0 after each gesture to prevent compounding effects.

5. Using UIRotationGestureRecognizer

Property Value Description
rotation CGFloat Current rotation angle in radians
view UIView? View that the gesture recognizer is attached to
numberOfTouchesRequired Int Number of touches required to trigger the gesture (default is 2)

Example: Detecting Rotation Gesture

class ViewController: UIViewController {
    @IBOutlet weak var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(handleRotation))
        imageView.addGestureRecognizer(rotationGesture)
        imageView.isUserInteractionEnabled = true
    }

    @objc private func handleRotation(_ gesture: UIRotationGestureRecognizer) {
        imageView.transform = imageView.transform.rotated(by: gesture.rotation)
        gesture.rotation = 0
    }
}
Note: The rotation property is cumulative and must be reset to zero after applying the rotation to prevent continuous rotation.

6. Using UILongPressGestureRecognizer

Property Value Description
minimumPressDuration 0.5 Minimum time (in seconds) the user must press and hold to trigger the gesture
numberOfTapsRequired 0 Number of taps required to recognize the gesture (0 for long press)
numberOfTouchesRequired 1 Number of touches required to recognize the gesture

Example: Setting up a long press gesture recognizer

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))
        longPressGesture.minimumPressDuration = 0.5
        view.addGestureRecognizer(longPressGesture)
    }
    
    @objc func handleLongPress(_ gesture: UILongPressGestureRecognizer) {
        switch gesture.state {
        case .began:
            print("Long press began")
        case .ended:
            print("Long press ended")
        default:
            break
        }
    }
}
Note: The UILongPressGestureRecognizer can be used to detect long press events on any view. The state changes include .began, .changed, and .ended.

7. Implementing Custom Gesture Recognizers

Creating custom gesture recognizers in iOS allows you to detect complex touch patterns that aren't covered by the built-in recognizers. This involves subclassing UIGestureRecognizer and implementing the necessary logic to detect your custom gesture.

Method Description Notes
touchesBegan(_:with:) Called when the first touch begins. Override to handle initial touch detection.
touchesMoved(_:with:) Called when touches move. Use to track movement and update gesture state.
touchesEnded(_:with:) Called when touches end. Finalize gesture recognition logic here.
touchesCancelled(_:with:) Called when touches are cancelled. Reset or cancel the gesture recognizer state.
state Current state of the gesture recognizer. Must be updated to .recognized, .failed, or .began.

Example: Custom Tap Gesture Recognizer

class CustomTapGestureRecognizer: UIGestureRecognizer {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        guard let touch = touches.first else { return }
        if touch.location(in: view).distance(to: center) <= 50 {
            state = .began
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
        guard state == .began else { return }
        state = .recognized
    }

    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
        state = .failed
    }
}

extension CGPoint {
    func distance(to point: CGPoint) -> CGFloat {
        return sqrt(pow(point.x - x, 2) + pow(point.y - y, 2))
    }
}
Note: Custom gesture recognizers must be added to a view using addGestureRecognizer(_:). The recognizer will receive touch events and update its state accordingly.

8. Handling Touch Events

Event Description Usage
touchesBegan Called when touch begins on the screen
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    print("Touch began")
}
touchesMoved Called when a touch moves on the screen
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    print("Touch moved")
}
touchesEnded Called when a touch ends on the screen
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    print("Touch ended")
}
touchesCancelled Called when a touch is cancelled (e.g., by system interruption)
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
    print("Touch cancelled")
}

Example: Detecting Touch Location

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first else { return }
    let location = touch.location(in: self.view)
    print("Touch at: \(location)")
}
Note: These methods are part of the UIResponder class and can be overridden in UIView or UIViewController.

9. Understanding Gesture Recognizer States

State Description Notes
possible Initial state; recognizer is waiting to determine if a gesture is occurring. Default state before any touch or movement begins.
began Gesture has been recognized and is about to begin. Triggered once when the gesture starts (e.g., first finger touches screen).
changed Gesture is in progress and has changed since last update. Repeatedly called as the gesture continues (e.g., finger moves).
ended Gesture has completed successfully. Triggered when the gesture ends (e.g., finger lifts off screen).
cancelled Gesture was interrupted or cancelled (e.g., system alert appears). Used to clean up or reset state when gesture is aborted.
failed Gesture failed to recognize and will not trigger. Used when the recognizer determines that the gesture is not valid (e.g., too few touches).

Example: Handling State Changes

let tapGesture = UITapGestureRecognizer()
tapGesture.addTarget(self, action: #selector(handleTap))
view.addGestureRecognizer(tapGesture)

@objc func handleTap(_ gesture: UITapGestureRecognizer) {
    switch gesture.state {
    case .began:
        print("Tap began")
    case .changed:
        print("Tap changed")
    case .ended:
        print("Tap ended")
    case .cancelled, .failed:
        print("Tap cancelled or failed")
    default:
        print("Unknown state")
    }
}

10. Combining Multiple Gestures

Example: Combining Tap and Long Press Gestures

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap))
        let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress))
        
        // Allow simultaneous recognition
        tapGesture.delaysTouchesBegan = false
        longPressGesture.delaysTouchesBegan = false
        
        view.addGestureRecognizer(tapGesture)
        view.addGestureRecognizer(longPressGesture)
    }
    
    @objc func handleTap() {
        print("Single tap recognized")
    }
    
    @objc func handleLongPress(_ gesture: UILongPressGestureRecognizer) {
        if gesture.state == .began {
            print("Long press started")
        }
    }
}
Gesture Description Simultaneous Recognition Use Case
Tap Gesture Recognizes single or multiple taps NEW in iOS 13+ Trigger actions on tap
Long Press Gesture Recognizes sustained touch Enabled by default Context menus, drag operations
Pan Gesture Recognizes drag or swipe Enabled by default Scrolling, dragging elements
Pinch Gesture Recognizes scaling with two fingers Enabled by default Zooming, resizing elements
Note: Use UIGestureRecognizerDelegate to control simultaneous gesture recognition behavior.

Best Practices for Gesture Combination

  • Enable delaysTouchesBegan = false for responsive feedback
  • Use require(toFail:) to prevent conflicts between gestures
  • Test combinations on actual devices for smooth interaction
  • Provide visual feedback when gestures are recognized

Was this article helpful?