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.