美文网首页
iOS Review | UIGestureRecognizer

iOS Review | UIGestureRecognizer

作者: 清無 | 来源:发表于2017-09-18 15:19 被阅读180次

    UIGestureRecognizer共有8种:

    • UITapGestureRecognizer
    • UIPanGestureRecognizerpan
    • UIScreenEdgePanRecognizer
    • UIPinchGestureRecognizer
    • UIRotationGestureRecognizer
    • UILongPressGestureRecognizer
    • UISwipeGestureRecognizer
    • UIGestureRecognizer即自定义手势

    其用法大同小异,比较常用的主要有tap和pan,主要使用recognizer.locationrecognizer.view属性。

    Storyboard上添加手势

    UIPanGestureRecognizer

    利用UIPanGesgtureRecognizer让view跟随touch移动,通常有两种处理逻辑。

    • 第一种利用recognizer.location
    var offset: CGSize! // 记住一开始的touch point距离center的偏移值
    
    // 事件回调
    @IBAction func handlePan(recognizer : UIPanGestureRecognizer) {
        let location = recognizer.location(in: view)
        guard let view = recognizer.view else {
            return
        }
        switch recognizer.state {
            case .began:
    // 计算并存储偏移值
                offset = CGSize(width: view.center.x - location.x, height: view.center.y - location.y)
    // 偏移值 + location值即可做到跟随移动
            case .changed:
                view.center = CGPoint(x: offset.width + location.x, y: offset.height + location.y)
            default: break
        }
    }
    
    recognizer.location

    这种方式虽然理解起来简单,但处理逻辑稍微繁琐点,不推荐使用。

    • 第一种利用recognizer.translation
    @IBAction func handlePan(recognizer : UIPanGestureRecognizer) { 
    // pan的移动偏移量--相对began时的点
        let translation = recognizer.translation(in: view)
        if let view = recognizer.view {
            view.center = CGPoint(x: view.center.x + translation.x, y: view.center.y + translation.y)
        }
    // 在移动changed时,重置pan的上一次移动点为zero
        recognizer.setTranslation(.zero, in: view)
    }
    

    这种方式虽然使用简单,但要注意每次recognizer.setTranslation(.zero)归零,不然,一下子就让view移出了屏幕,因为translation每次就compound叠加的。

    • pan手势滑动的加速度velocity
    if recognizer.state == .ended {
    // 加速度
        let velocity = recognizer.velocity(in: self.view)
        let magnitude = sqrt(velocity.x * velocity.x + velocity.y * velocity.y)
        let slideMultiplier = magnitude / 200
        let slideFactor = 0.1 * slideMultiplier
        
    // 最终点
        var finalPoint = CGPoint(x: view.center.x + (velocity.x * slideFactor), y: view.center.y + (velocity.y * slideFactor))
        let halfWidth = panView.bounds.width / 2
        let halfHeight = panView.bounds.height / 2
        finalPoint.x = min(self.view.bounds.width - halfWidth, max(halfWidth, finalPoint.x))
        finalPoint.y = min(self.view.bounds.height - halfHeight, max(halfHeight, finalPoint.y))
        
    // 动画
        UIView.animate(withDuration: Double(slideFactor * 2), delay: 0, options: .curveEaseInOut, animations: {
            panView.center = finalPoint
        }, completion: nil)
    }
    
    加速度动画

    UIPinchGestureRecognizer

    • 使用比较简单,利用recognizer.scale值即可transform要scale的view,但注意scale值也是连续变化的,注意随时将recognizer.scale归零。
      @IBAction func handlePinch(recognizer : UIPinchGestureRecognizer) {
        guard let pinchView = recognizer.view else {
            return
        }
        let scale = recognizer.scale
        pinchView.transform = pinchView.transform.scaledBy(x: scale, y: scale)
        recognizer.scale = 1 // 归零
      }
    

    UIRotationGestureRecognizer

    • 使用和UIPinchGestureRecognizer一样,利用recognizer.rotation值即可transform要rotate的view,但注意rotation值也是连续变化的,注意随时将recognizer.rotation归零。
      @IBAction func handleRotate(recognizer : UIRotationGestureRecognizer) {
        guard let rotateView = recognizer.view else {
            return
        }
        let rotation = recognizer.rotation
        rotateView.transform = rotateView.transform.rotated(by: rotation)
        recognizer.rotation = 0
      }
    

    Simultaneous Gesture Recognizers

    • 一般情况下,每个手势只能被单独使用,并不能在执行一个手势如rotation的同时执行scale手势,但可以设置UIGestureRecognizer的delegate,来配置是否允许手势同时执行。
    extension ViewController: UIGestureRecognizerDelegate{
        func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
            return true
        }
    }
    

    UITapGestureRecognizer

    • 这个手势相对来说是最常用的一个,实现方式也简单,常用recognizer.location属性和recognizer.view判断点击的view是否是目标view,然后处理不同的逻辑。
    var chompPlayer: AVAudioPlayer? = nil
        
      override func viewDidLoad() {
        super.viewDidLoad()
        
        let filteredSubviews = view.subviews.filter{
            $0 is UIImageView
        }
    // 给所有UIImageView添加tap手势
        for subview in filteredSubviews {
            let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(recognizer:)))
            tapGestureRecognizer.delegate = self
            subview.addGestureRecognizer(tapGestureRecognizer)
    
    // TODO:
        }
        
        chompPlayer = loadSound(filename: "chomp")
      }
    
    // tap手势处理
    @objc func handleTap(recognizer: UITapGestureRecognizer) {
        chompPlayer?.play()
      }
    
    • 但注意以上tap手势和pan手势会同时执行,在pan很小值得时候,tap手势也会被触发,这种情况下可以用recognizer.require(toFail:)让2个冲突的手势只能执行一个。
    // TODO:
    tapGestureRecognizer.require(toFail: panGestureRecognizer)
    

    Custom UIGestureRecognizer

    • 基于UIGestureRecognizer的自定义手势,注意在Swift中,需要借助OC桥接.h文件,才能重写touches等事件方法。
    1. 新建OC-Header桥接文件,并导入头文件。
    #import <UIKit/UIGestureRecognizerSubclass.h>
    
    1. 新建类,实现touchesBegan、moved、ended、canceled等方法。

    "挠痒痒"自定义手势

    class TickleGestureRecognizer: UIGestureRecognizer {
        enum Direction: String {
            case unknown = "DirectionUnknown", 
            left = "DirectionLeft", 
            right = "DirectionRight"
        }
        var requiredTickles = 2
        var distanceForTickleGesture: CGFloat = 25
        
        var tickleCount = 0
        var lastDirection: Direction = .unknown
        var curTickleStart: CGPoint = .zero
        
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
            guard let touch = touches.first else {
                return
            }
            curTickleStart = touch.location(in: view)
        }
        
        override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
            guard let touch = touches.first else {
                return
            }
            let ticklePoint = touch.location(in: view)
            let moveAmt = ticklePoint.x - curTickleStart.x 
            var curDirection: Direction = .unknown
            if moveAmt < 0 {
                curDirection = .left
            }
            else{
                curDirection = .right
            }
            if fabs(moveAmt) < distanceForTickleGesture {
                return
            }
            
            if (lastDirection == .left && curDirection == .right) ||
                (lastDirection == .right && curDirection == .left) || 
                lastDirection == .unknown{
                tickleCount += 1
                curTickleStart = ticklePoint
                lastDirection = curDirection
                
                if state == .possible && tickleCount > requiredTickles{
                    print("He He He...")
                    state = .ended
                }
            }
            
            print("\(curDirection.rawValue)")
        }
        
        override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
            reset()
        }
        
        override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
            reset()
        }
        
        override func reset() {
            curTickleStart = .zero
            lastDirection = .unknown
            tickleCount = 0
            if state == .possible {
                state = .failed
            }
        }
    }
    
    1. 使用自定义手势
    let tickleGestureRecognizer = TickleGestureRecognizer(target: self, action: #selector(handleTickle(recognizer:)))
    subview.addGestureRecognizer(tickleGestureRecognizer)
    
    @objc func handleTickle(recognizer: TickleGestureRecognizer) {
        hehePlayer?.play()
    }
    
    也可以在storyboard上使用自定义手势

    UILongPressGestureRecognizer

    长按手势

    UIScreenEdgePanGestureRecognizer

    屏幕边缘滑动手势

    UISwipeGestureRecognizer

    扫除手势

    • 支持单点和多点手势,设置numberOfTouchesRequired属性。
    • 判断方向通过direction属性,主要有up、down、left、right
    • 可以通过recognizer.location进行子view的translation变换。

    相关文章

      网友评论

          本文标题:iOS Review | UIGestureRecognizer

          本文链接:https://www.haomeiwen.com/subject/tlivsxtx.html