Gesture recognition lets users interact with your app by touching the screen in natural ways like dragging, pinching, or rotating. It makes apps feel alive and easy to use.
Gesture recognition (drag, magnify, rotate) in iOS Swift
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan)) view.addGestureRecognizer(panGesture) let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch)) view.addGestureRecognizer(pinchGesture) let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(handleRotation)) view.addGestureRecognizer(rotationGesture) @objc func handlePan(_ gesture: UIPanGestureRecognizer) { // code to move view } @objc func handlePinch(_ gesture: UIPinchGestureRecognizer) { // code to zoom view } @objc func handleRotation(_ gesture: UIRotationGestureRecognizer) { // code to rotate view }
handlePan runs.let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
view.addGestureRecognizer(panGesture)handlePinch method will be called.let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch))
view.addGestureRecognizer(pinchGesture)handleRotation method will handle it.let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(handleRotation))
view.addGestureRecognizer(rotationGesture)This app shows a blue square you can drag, pinch to zoom, and rotate with your fingers. The gestures update the square's position, size, and angle smoothly.
import UIKit class ViewController: UIViewController { let square = UIView() var currentRotation: CGFloat = 0 var currentScale: CGFloat = 1 var currentTranslation = CGPoint.zero override func viewDidLoad() { super.viewDidLoad() square.frame = CGRect(x: 100, y: 100, width: 150, height: 150) square.backgroundColor = .systemBlue view.addSubview(square) let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan)) square.addGestureRecognizer(panGesture) let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch)) square.addGestureRecognizer(pinchGesture) let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(handleRotation)) square.addGestureRecognizer(rotationGesture) square.isUserInteractionEnabled = true } @objc func handlePan(_ gesture: UIPanGestureRecognizer) { let translation = gesture.translation(in: view) if gesture.state == .changed || gesture.state == .ended { let newX = currentTranslation.x + translation.x let newY = currentTranslation.y + translation.y square.transform = CGAffineTransform.identity .translatedBy(x: newX, y: newY) .scaledBy(x: currentScale, y: currentScale) .rotated(by: currentRotation) } if gesture.state == .ended { currentTranslation.x += translation.x currentTranslation.y += translation.y } } @objc func handlePinch(_ gesture: UIPinchGestureRecognizer) { if gesture.state == .changed || gesture.state == .ended { let scale = currentScale * gesture.scale square.transform = CGAffineTransform.identity .translatedBy(x: currentTranslation.x, y: currentTranslation.y) .scaledBy(x: scale, y: scale) .rotated(by: currentRotation) } if gesture.state == .ended { currentScale *= gesture.scale gesture.scale = 1 } } @objc func handleRotation(_ gesture: UIRotationGestureRecognizer) { if gesture.state == .changed || gesture.state == .ended { let rotation = currentRotation + gesture.rotation square.transform = CGAffineTransform.identity .translatedBy(x: currentTranslation.x, y: currentTranslation.y) .scaledBy(x: currentScale, y: currentScale) .rotated(by: rotation) } if gesture.state == .ended { currentRotation += gesture.rotation gesture.rotation = 0 } } }
Remember to enable user interaction on views you want to gesture on by setting isUserInteractionEnabled = true.
Gesture recognizers can work together, but sometimes you need to manage conflicts.
Use the gesture's state to update your view only when the gesture changes or ends.
Gesture recognizers detect user touches like drag, pinch, and rotate.
Add gesture recognizers to views and handle their actions to update UI.
Combine translation, scale, and rotation transforms to move and change views smoothly.
Practice
Solution
Step 1: Understand gesture types
DragGesture detects dragging motions, MagnificationGesture detects pinch zoom, RotationGesture detects rotation, and TapGesture detects taps.Step 2: Match gesture to dragging
Since dragging means moving finger across the screen, DragGesture is the correct recognizer.Final Answer:
DragGesture -> Option CQuick Check:
DragGesture = dragging motion [OK]
- Confusing MagnificationGesture with dragging
- Choosing TapGesture for drag
- Mixing RotationGesture with drag
Solution
Step 1: Identify rotation gesture syntax
RotationGesture() is the correct gesture for rotation, and .gesture() modifier adds it to a view.Step 2: Confirm onChanged closure usage
onChanged receives the rotation angle, so the syntax .gesture(RotationGesture().onChanged { angle in ... }) is correct.Final Answer:
.gesture(RotationGesture().onChanged { angle in /* code */ }) -> Option AQuick Check:
RotationGesture syntax = .gesture(RotationGesture().onChanged { angle in /* code */ }) [OK]
- Using DragGesture instead of RotationGesture
- Using .onTapGesture for rotation
- Confusing MagnificationGesture with rotation
Image("photo")
.scaleEffect(scale)
.rotationEffect(angle)
.offset(dragAmount)
.gesture(
DragGesture()
.onChanged { dragAmount = $0.translation }
.onEnded { _ in dragAmount = .zero }
)
.gesture(
MagnificationGesture()
.onChanged { scale = $0 }
)
.gesture(
RotationGesture()
.onChanged { angle = $0 }
)Solution
Step 1: Analyze gesture modifiers
The code adds three gestures: DragGesture updates dragAmount offset, MagnificationGesture updates scale, RotationGesture updates angle.Step 2: Understand combined effects
The view applies scaleEffect, rotationEffect, and offset using these state variables, so all gestures affect the image interactively.Final Answer:
The image can be dragged, zoomed, and rotated by user gestures. -> Option BQuick Check:
All gestures update view transforms = The image can be dragged, zoomed, and rotated by user gestures. [OK]
- Assuming only one gesture works at a time
- Ignoring that gestures update state variables
- Thinking gestures override each other
Circle()
.gesture(DragGesture().onChanged { value in
dragAmount = value.translation
})
.gesture(MagnificationGesture().onChanged { scale = $0 })Solution
Step 1: Understand gesture modifiers stacking
Applying multiple .gesture modifiers separately causes only the last one to be active.Step 2: Identify which gesture is active
Since MagnificationGesture is added last, it overrides DragGesture, so only magnification works.Final Answer:
Only the MagnificationGesture will work; DragGesture is ignored. -> Option AQuick Check:
Last .gesture overrides previous = Only the MagnificationGesture will work; DragGesture is ignored. [OK]
- Assuming both gestures work independently
- Expecting compile error for multiple gestures
- Thinking first gesture always wins
Solution
Step 1: Understand gesture combination in SwiftUI
Multiple .gesture modifiers override each other, so to combine gestures, use .simultaneousGesture.Step 2: Apply simultaneousGesture for all three
By nesting .simultaneousGesture for DragGesture, MagnificationGesture, and RotationGesture, all gestures work together smoothly.Final Answer:
Use .simultaneousGesture to combine DragGesture, MagnificationGesture, and RotationGesture on the view. -> Option DQuick Check:
Combine gestures with simultaneousGesture = Use .simultaneousGesture to combine DragGesture, MagnificationGesture, and RotationGesture on the view. [OK]
- Adding multiple .gesture modifiers separately
- Expecting one gesture to handle all types
- Ignoring zoom and rotate gestures
Ready to prove it?
Pass the test and this pattern moves unknown
