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 | |
| touchesMoved | Called when a touch moves on the screen | |
| touchesEnded | Called when a touch ends on the screen | |
| touchesCancelled | Called when a touch is cancelled (e.g., by system interruption) | |
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.