【问题标题】:Pinch, Pan, and Rotate Text Simultaneously like Snapchat [SWIFT 3]像 Snapchat 一样同时捏合、平移和旋转文本 [SWIFT 3]
【发布时间】:2017-07-30 17:23:56
【问题描述】:

我正在尝试制作一个可以像 snapchat 一样移动的 TextView。我做了一些类似的东西,虽然当你尝试在旋转的同时进行缩放,它往往会无限地水平拉伸,并且有时拖动可能会有点麻烦。

我有这个:

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
}

我正在努力做到这一点:

如果有人可以帮助更改或重新编写我的代码,那就太好了,在此先感谢!

【问题讨论】:

    标签: ios swift text gesture snapchat


    【解决方案1】:

    默认情况下,在视图上的一个手势识别器开始处理手势后,其他识别器将被忽略。在 Swift 中,这种行为可以通过重写方法来控制,

    gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:)^1,

    返回 true。默认实现返回 false。

    要覆盖该函数,只需将您的实现(返回 true)添加到您的 ViewController 源代码文件中。这是一些示例 Swift 代码:

    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
        }
    }
    

    这是 Objective-C^2 中的关键覆盖:

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

    【讨论】:

    • 如果你在 swift 4 中使用它,你需要在每个句柄函数之前添加 @objc。宣言。它曾经在 swift 4 之前被隐式推断。
    • 如果您想捕捉最终的坐标/大小/旋转/等,以便您可以将其相应地放置在帖子或其他任何东西上,类似于 Snapchat,您会怎么做?
    • 对我来说,文本字段没有旋转,但平移手势正在工作
    • @Nikhlesh Bagdiya 你能提供objective-c代码吗?
    【解决方案2】:

    我写了一个易于使用的类。

    基本用法:

    // define 
    var snapGesture: SnapGesture?
    
    // add gesture
    self.snapGesture = SnapGesture(view: self.testView!)
    
    // remove gesture
    self.snapGesture = nil
    

    高级用法,对于视图接收手势为背景视图的场景:

    // add gesture
    self.snapGesture = SnapGesture(transformView: self.testView!, gestureView: self.view)
    
    // remove gesture
    self.snapGesture = nil
    

    类:

    import UIKit
    
    /*
     usage:
    
        add gesture:
            yourObjToStoreMe.snapGesture = SnapGesture(view: your_view)
        remove gesture:
            yourObjToStoreMe.snapGesture = nil
        disable gesture:
            yourObjToStoreMe.snapGesture.isGestureEnabled = false
        advanced usage:
            view to receive gesture(usually superview) is different from view to be transformed,
            thus you can zoom the view even if it is too small to be touched.
            yourObjToStoreMe.snapGesture = SnapGesture(transformView: your_view_to_transform, gestureView: your_view_to_recieve_gesture)
    
     */
    
    class SnapGesture: NSObject, UIGestureRecognizerDelegate {
    
        // MARK: - init and deinit
        convenience init(view: UIView) {
            self.init(transformView: view, gestureView: view)
        }
        init(transformView: UIView, gestureView: UIView) {
            super.init()
    
            self.addGestures(v: gestureView)
            self.weakTransformView = transformView
        }
        deinit {
            self.cleanGesture()
        }
    
        // MARK: - private method
        private weak var weakGestureView: UIView?
        private weak var weakTransformView: UIView?
    
        private var panGesture: UIPanGestureRecognizer?
        private var pinchGesture: UIPinchGestureRecognizer?
        private var rotationGesture: UIRotationGestureRecognizer?
    
        private func addGestures(v: UIView) {
    
            panGesture = UIPanGestureRecognizer(target: self, action: #selector(panProcess(_:)))
            v.isUserInteractionEnabled = true
            panGesture?.delegate = self     // for simultaneous recog
            v.addGestureRecognizer(panGesture!)
    
            pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(pinchProcess(_:)))
            //view.isUserInteractionEnabled = true
            pinchGesture?.delegate = self   // for simultaneous recog
            v.addGestureRecognizer(pinchGesture!)
    
            rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(rotationProcess(_:)))
            rotationGesture?.delegate = self
            v.addGestureRecognizer(rotationGesture!)
    
            self.weakGestureView = v
        }
    
        private func cleanGesture() {
            if let view = self.weakGestureView {
                //for recognizer in view.gestureRecognizers ?? [] {
                //    view.removeGestureRecognizer(recognizer)
                //}
                if panGesture != nil {
                    view.removeGestureRecognizer(panGesture!)
                    panGesture = nil
                }
                if pinchGesture != nil {
                    view.removeGestureRecognizer(pinchGesture!)
                    pinchGesture = nil
                }
                if rotationGesture != nil {
                    view.removeGestureRecognizer(rotationGesture!)
                    rotationGesture = nil
                }
            }
            self.weakGestureView = nil
            self.weakTransformView = nil
        }
    
    
    
    
        // MARK: - API
    
        private func setView(view:UIView?) {
            self.setTransformView(view, gestgureView: view)
        }
    
        private func setTransformView(_ transformView: UIView?, gestgureView:UIView?) {
            self.cleanGesture()
    
            if let v = gestgureView  {
                self.addGestures(v: v)
            }
            self.weakTransformView = transformView
        }
    
        open func resetViewPosition() {
            UIView.animate(withDuration: 0.4) {
                self.weakTransformView?.transform = CGAffineTransform.identity
            }
        }
    
        open var isGestureEnabled = true
    
        // MARK: - gesture handle
    
        // location will jump when finger number change
        private var initPanFingerNumber:Int = 1
        private var isPanFingerNumberChangedInThisSession = false
        private var lastPanPoint:CGPoint = CGPoint(x: 0, y: 0)
        @objc func panProcess(_ recognizer:UIPanGestureRecognizer) {
            if isGestureEnabled {
                //guard let view = recognizer.view else { return }
                guard let view = self.weakTransformView else { return }
    
                // init
                if recognizer.state == .began {
                    lastPanPoint = recognizer.location(in: view)
                    initPanFingerNumber = recognizer.numberOfTouches
                    isPanFingerNumberChangedInThisSession = false
                }
    
                // judge valid
                if recognizer.numberOfTouches != initPanFingerNumber {
                    isPanFingerNumberChangedInThisSession = true
                }
                if isPanFingerNumberChangedInThisSession {
                    return
                }
    
                // perform change
                let point = recognizer.location(in: view)
                view.transform = view.transform.translatedBy(x: point.x - lastPanPoint.x, y: point.y - lastPanPoint.y)
                lastPanPoint = recognizer.location(in: view)
            }
        }
    
    
    
        private var lastScale:CGFloat = 1.0
        private var lastPinchPoint:CGPoint = CGPoint(x: 0, y: 0)
        @objc func pinchProcess(_ recognizer:UIPinchGestureRecognizer) {
            if isGestureEnabled {
                guard let view = self.weakTransformView else { return }
    
                // init
                if recognizer.state == .began {
                    lastScale = 1.0;
                    lastPinchPoint = recognizer.location(in: view)
                }
    
                // judge valid
                if recognizer.numberOfTouches < 2 {
                    lastPinchPoint = recognizer.location(in: view)
                    return
                }
    
                // Scale
                let scale = 1.0 - (lastScale - recognizer.scale);
                view.transform = view.transform.scaledBy(x: scale, y: scale)
                lastScale = recognizer.scale;
    
                // Translate
                let point = recognizer.location(in: view)
                view.transform = view.transform.translatedBy(x: point.x - lastPinchPoint.x, y: point.y - lastPinchPoint.y)
                lastPinchPoint = recognizer.location(in: view)
            }
        }
    
    
        @objc func rotationProcess(_ recognizer: UIRotationGestureRecognizer) {
            if isGestureEnabled {
                guard let view = self.weakTransformView else { return }
    
                view.transform = view.transform.rotated(by: recognizer.rotation)
                recognizer.rotation = 0
            }
        }
    
    
        //MARK:- UIGestureRecognizerDelegate Methods
        func gestureRecognizer(_: UIGestureRecognizer,
                               shouldRecognizeSimultaneouslyWith shouldRecognizeSimultaneouslyWithGestureRecognizer:UIGestureRecognizer) -> Bool {
            return true
        }
    
    }
    

    现在,您已经到了这一点,请继续。 如您所知,superView 中的手势可能会吃掉 subView 的事件,而在 snapchat 示例中,手势会吃掉工具栏的事件,如果我们触摸工具栏,我们需要防止 superview 的任何手势识别。

    这个想法是在工具栏上添加一个pseduo自定义手势,因此任何手势都将被阻止到superview,并且这个pseduo手势除了将手势或事件传递给子视图或它自己的视图之外什么都不做。

    在这里,我也写了一个类,方便使用。

    用法:

       toolbarView.addGestureRecognizer(SnapBlockGestureRecognizer)
    

    实现:

    import UIKit
    
    class SnapBlockGestureRecognizer: UIGestureRecognizer {
    
        init() {
            //self.init(target: self, action: #selector(__dummyAction))
            super.init(target: nil, action: nil)
    
            self.addTarget(self, action: #selector(__dummyAction))
            self.cancelsTouchesInView = false
        }
    
        override init(target: Any?, action: Selector?) {
            super.init(target: target, action: action)
    
            self.cancelsTouchesInView = false
        }
    
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
            if self.state == .possible {
                self.state = .began
            }
        }
    
        override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
        }
    
        override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
            self.state = .recognized
        }
    
        override func canBePrevented(by preventingGestureRecognizer: UIGestureRecognizer) -> Bool {
            return self.isGestureRecognizerAllowed(gr:preventingGestureRecognizer)
        }
    
    
        override func canPrevent(_ preventedGestureRecognizer: UIGestureRecognizer) -> Bool {
            return !(self.isGestureRecognizerAllowed(gr: preventedGestureRecognizer))
        }
    
        override func shouldBeRequiredToFail(by otherGestureRecognizer: UIGestureRecognizer) -> Bool {
            return !(self.isGestureRecognizerAllowed(gr: otherGestureRecognizer))
        }
    
        override func shouldRequireFailure(of otherGestureRecognizer: UIGestureRecognizer) -> Bool {
            return false
        }
    
        func isGestureRecognizerAllowed(gr: UIGestureRecognizer) -> Bool {
            return gr.view!.isDescendant(of: self.view!)
        }
    
        @objc func __dummyAction() {
            // do nothing
            // print("dummyAction")
        }
    }
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-13
    • 1970-01-01
    • 2012-04-12
    • 1970-01-01
    相关资源
    最近更新 更多