0
0
iOS Swiftmobile~20 mins

Gesture recognition (drag, magnify, rotate) in iOS Swift - Mini App: Build & Ship

Choose your learning style9 modes available
Build: Gesture Demo Screen
This screen lets the user drag, pinch to zoom, and rotate a square view using gestures.
Target UI
┌─────────────────────────────┐
│                             │
│          [  ■  ]            │
│                             │
│  Drag, pinch, or rotate the  │
│  square above with your      │
│  fingers.                    │
│                             │
└─────────────────────────────┘
Add a square UIView centered on the screen with a distinct background color.
Enable dragging the square around the screen with a pan gesture.
Enable pinch gesture to magnify (zoom) the square.
Enable rotation gesture to rotate the square.
All gestures should work smoothly and simultaneously.
The square should stay visible within the screen bounds.
Starter Code
iOS Swift
import UIKit

class GestureDemoViewController: UIViewController {
    let squareView = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        squareView.backgroundColor = .systemBlue
        squareView.frame = CGRect(x: 0, y: 0, width: 150, height: 150)
        squareView.center = view.center
        view.addSubview(squareView)

        // TODO: Add gesture recognizers here
    }
}
Task 1
Task 2
Task 3
Task 4
Task 5
Solution
iOS Swift
import UIKit

class GestureDemoViewController: UIViewController, UIGestureRecognizerDelegate {
    let squareView = UIView()
    var currentTransform = CGAffineTransform.identity

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        squareView.backgroundColor = .systemBlue
        squareView.frame = CGRect(x: 0, y: 0, width: 150, height: 150)
        squareView.center = view.center
        view.addSubview(squareView)

        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
        panGesture.delegate = self
        squareView.addGestureRecognizer(panGesture)

        let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:)))
        pinchGesture.delegate = self
        squareView.addGestureRecognizer(pinchGesture)

        let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(handleRotation(_:)))
        rotationGesture.delegate = self
        squareView.addGestureRecognizer(rotationGesture)

        squareView.isUserInteractionEnabled = true
    }

    @objc func handlePan(_ gesture: UIPanGestureRecognizer) {
        let translation = gesture.translation(in: view)
        if gesture.state == .began || gesture.state == .changed {
            let translatedTransform = currentTransform.translatedBy(x: translation.x, y: translation.y)
            squareView.transform = translatedTransform
        } else if gesture.state == .ended || gesture.state == .cancelled {
            currentTransform = squareView.transform
            keepSquareInsideBounds()
        }
    }

    @objc func handlePinch(_ gesture: UIPinchGestureRecognizer) {
        if gesture.state == .began || gesture.state == .changed {
            let scaledTransform = currentTransform.scaledBy(x: gesture.scale, y: gesture.scale)
            squareView.transform = scaledTransform
        } else if gesture.state == .ended || gesture.state == .cancelled {
            currentTransform = squareView.transform
            keepSquareInsideBounds()
        }
    }

    @objc func handleRotation(_ gesture: UIRotationGestureRecognizer) {
        if gesture.state == .began || gesture.state == .changed {
            let rotatedTransform = currentTransform.rotated(by: gesture.rotation)
            squareView.transform = rotatedTransform
        } else if gesture.state == .ended || gesture.state == .cancelled {
            currentTransform = squareView.transform
            keepSquareInsideBounds()
        }
    }

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }

    private func keepSquareInsideBounds() {
        var frame = squareView.frame
        let superBounds = view.bounds

        if frame.minX < superBounds.minX {
            frame.origin.x = superBounds.minX
        }
        if frame.maxX > superBounds.maxX {
            frame.origin.x = superBounds.maxX - frame.width
        }
        if frame.minY < superBounds.minY {
            frame.origin.y = superBounds.minY
        }
        if frame.maxY > superBounds.maxY {
            frame.origin.y = superBounds.maxY - frame.height
        }

        UIView.animate(withDuration: 0.2) {
            self.squareView.frame = frame
        }
        currentTransform = self.squareView.transform
    }
}

We created a blue square view centered on the screen. We added three gesture recognizers: pan for dragging, pinch for zooming, and rotation for rotating the square. Each gesture updates the square's transform property to move, scale, or rotate it.

We keep track of the current transform so gestures build on the previous state. The gesture recognizer delegate allows all gestures to work simultaneously.

After gestures end, we check if the square is still fully visible inside the screen bounds and adjust its position if needed, so it never goes off-screen.

This approach gives a smooth and natural interaction for dragging, zooming, and rotating the square.

Final Result
Completed Screen
┌─────────────────────────────┐
│                             │
│          [  ■  ]            │
│                             │
│  Drag, pinch, or rotate the  │
│  square above with your      │
│  fingers.                    │
│                             │
└─────────────────────────────┘
User drags the blue square around the screen with one finger.
User pinches with two fingers to zoom the square bigger or smaller.
User rotates with two fingers to spin the square.
All gestures can be combined smoothly.
The square stays fully visible inside the screen edges.
Stretch Goal
Add a reset button that returns the square to its original size, rotation, and center position.
💡 Hint
Add a UIButton below the square. On tap, set squareView.transform to identity and center it again.