Skip to content

Instantly share code, notes, and snippets.

@osmanmesutozcan
Created January 10, 2024 11:14
Show Gist options
  • Save osmanmesutozcan/94de686dc80122aa4e23d0cf6b1ea0a7 to your computer and use it in GitHub Desktop.
Save osmanmesutozcan/94de686dc80122aa4e23d0cf6b1ea0a7 to your computer and use it in GitHub Desktop.
Use UIKit gestures on SwiftUI Views
struct ZoomableView<Content: View>: UIViewControllerRepresentable {
let gestures: [ZoomableViewGestureType]
@ViewBuilder let content: () -> Content
func makeUIViewController(context: Context) -> ZoomableViewController<Content> {
let vc = ZoomableViewController<Content>()
vc.gestures = gestures
vc.child = UIHostingController(rootView: content())
return vc
}
func updateUIViewController(_ vc: ZoomableViewController<Content>, context: Context) {
vc.child.rootView = content()
}
}
class ZoomableViewController<Content: View>: UIViewController, UIGestureRecognizerDelegate {
var gestures = [ZoomableViewGestureType]()
var child: UIHostingController<Content>!
override func viewDidLoad() {
super.viewDidLoad()
// Attach to self
addChild(child)
view.addSubview(child.view)
// Prepare child view
child.view.backgroundColor = .clear
child.view.isMultipleTouchEnabled = true
child.view.isUserInteractionEnabled = true
child.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
child.view.centerXAnchor.constraint(equalTo: view.centerXAnchor),
child.view.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
if gestures.contains(.pan) {
//add pan gesture
let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
gestureRecognizer.delegate = self
child.view.addGestureRecognizer(gestureRecognizer)
}
if gestures.contains(.zoom) {
//add pinch gesture
let pinchGesture = UIPinchGestureRecognizer(target: self, action:#selector(pinchRecognized(pinch:)))
pinchGesture.delegate = self
child.view.addGestureRecognizer(pinchGesture)
}
if gestures.contains(.rotate) {
//add rotate gesture.
let rotate = UIRotationGestureRecognizer.init(target: self, action: #selector(handleRotate(recognizer:)))
rotate.delegate = self
child.view.addGestureRecognizer(rotate)
}
}
@objc
func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) {
if gestureRecognizer.state == .began || gestureRecognizer.state == .changed {
let translation = gestureRecognizer.translation(in: self.view)
// note: 'view' is optional and need to be unwrapped
gestureRecognizer.view!.center = CGPoint(x: gestureRecognizer.view!.center.x + translation.x, y: gestureRecognizer.view!.center.y + translation.y)
gestureRecognizer.setTranslation(CGPoint.zero, in: self.view)
}
}
@objc
func pinchRecognized(pinch: UIPinchGestureRecognizer) {
if let view = pinch.view {
view.transform = view.transform.scaledBy(x: pinch.scale, y: pinch.scale)
pinch.scale = 1
}
}
@objc
func handleRotate(recognizer : UIRotationGestureRecognizer) {
if let view = recognizer.view {
view.transform = view.transform.rotated(by: recognizer.rotation)
recognizer.rotation = 0
}
}
//MARK:- UIGestureRecognizerDelegate Methods
func gestureRecognizer(_: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith shouldRecognizeSimultaneouslyWithGestureRecognizer:UIGestureRecognizer) -> Bool {
return true
}
}
enum ZoomableViewGestureType {
case pan
case zoom
case rotate
}
@osmanmesutozcan
Copy link
Author

Pass any SwiftUI View as a child.

ZStack {
    SomeBackgroundView()
        .background(.red)
        .frame(width: size.width, height: size.height)

    ZoomableView(gestures: [.pan, .zoom]) {
        Rectangle()
            .fill(Color.white)
            .frame(width: 100, height: 100)
    }
    .frame(maxWidth: .infinity, maxHeight: .infinity)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment