Search code examples
iosswifttextgesturesnapchat

Pinch, Pan, and Rotate Text Simultaneously like Snapchat [SWIFT 3]


I'm trying to make a TextView that you can move around like snapchat does. I have made something similar to it, although when you try and scale while rotates, it tends to stretch horizontally infinitely and dragging can be a little buggy at times.

I have this:

func panGesture(pan: UIPanGestureRecognizer) {
    print("Being Dragged")
    if pan.state == .began {
        textViewOrigin = pan.location(in: textView)
    }else {
        let location = pan.location(in: view) // get pan location
        textView.frame.origin = CGPoint(x: location.x - textViewOrigin.x, y: location.y - textViewOrigin.y)
    }
}
func scaleGesture(_ gesture: UIPinchGestureRecognizer){
    print("Being Scaled")
    switch gesture.state{
    case.began:
        identity = textView.transform
    case.changed,.ended:
        textView.transform = identity.scaledBy(x: gesture.scale, y: gesture.scale)
    default:
        break
    }
}
func rotationGesture(sender: UIRotationGestureRecognizer){
    print("Being Rotated")
    textView.transform = textView.transform.rotated(by: sender.rotation)
    sender.rotation = 0
}

and I am trying to accomplish this: enter image description here

If someone could help alter or re-write my code that would be great, thanks in advance!


Solution

  • By default, after one gesture recognizer on a view starts handling the gesture, other recognizers are ignored. In Swift, this behaviour can be controlled by overriding the method,

    gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:)^1,

    to return true. The default implementation returns false.

    To override the function just add your implementation, returning true, to your ViewController source code file. Here is some sample Swift code:

    class ViewController: UIViewController,UIGestureRecognizerDelegate {
    
        @IBOutlet var textField: UITextField!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
    
            //add pan gesture
            let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
            gestureRecognizer.delegate = self
            textField.addGestureRecognizer(gestureRecognizer)
    
            //Enable multiple touch and user interaction for textfield
            textField.isUserInteractionEnabled = true
            textField.isMultipleTouchEnabled = true
    
            //add pinch gesture
            let pinchGesture = UIPinchGestureRecognizer(target: self, action:#selector(pinchRecognized(pinch:)))
            pinchGesture.delegate = self
            textField.addGestureRecognizer(pinchGesture)
    
            //add rotate gesture.
            let rotate = UIRotationGestureRecognizer.init(target: self, action: #selector(handleRotate(recognizer:)))
            rotate.delegate = self
            textField.addGestureRecognizer(rotate)
    
    
        }
    
        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)
            }
    
        }
    
        func pinchRecognized(pinch: UIPinchGestureRecognizer) {
    
            if let view = pinch.view {
                view.transform = view.transform.scaledBy(x: pinch.scale, y: pinch.scale)
                pinch.scale = 1
            }
        }
    
        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
        }
    }
    

    Here's the crucial override in Objective-C^2:

    -(BOOL)gestureRecognizer:(UIGestureRecognizer*)aR1 shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)aR2
    {
        return YES;
    }