iOS文档补完计划--UIGestureRecognizer

作者: kirito_song | 来源:发表于2018-10-22 15:31 被阅读33次

    目录

    • UIGestureRecognizerDelegate
    • 调节手势识别
      • gestureRecognizerShouldBegin:
      • gestureRecognizer:shouldReceiveTouch:
    • 多手势触发
      • gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
      • gestureRecognizer:shouldRequireFailureOfGestureRecognizer:
      • gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:
    • UIGestureRecognizer
      • 手势识别流程
      • 6个子类手势
      • Action
    • 初始化
      • initWithTarget:action:
    • 添加和移除Target&&Action
      • addTarget:action:
      • removeTarget:action
    • 获取手势的触摸和位置
      • locationInView:
      • locationOfTouch:inView:
      • numberOfTouches
    • 获取手势的状态和View
      • state
      • view
      • enabled
    • 取消和延迟触摸
      • cancelsTouchesInView
      • delaysTouchesBegan
      • delaysTouchesEnded
    • 添加依赖
      • requireGestureRecognizerToFail:

    UIGestureRecognizerDelegate

    你可以通过代理方法、去细致的定制一些识别行为

    比如是否触发手势识别、是否进行手势识别。多手势冲突如何处理等

    @protocol UIGestureRecognizerDelegate
    

    调节手势识别

    • - gestureRecognizerShouldBegin:

    是否继续进行手势识别。默认YES

    - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer;
    

    返回NO则结束识别,不再触发手势。
    可以在控件指定的位置开启手势识别

    • - gestureRecognizer:shouldReceiveTouch:

    window对象在有触摸事件发生时。默认YES

    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
           shouldReceiveTouch:(UITouch *)touch;
    

    位于touchesBegan:withEvent:之前被调用。
    如果返回NO、该事件将不会被通知给GestureRecognizer


    多手势触发

    • - gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:

    是否支持多手势触发。默认NO。

    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
    shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
    

    正常情况下只会有一个识别器进行手势识别、也就是上层对象识别后则不再继续传播。
    如果返回YES、响应者链上层对象触发手势识别后、如果下层对象也添加了手势并成功识别也会继续执行。

    • gestureRecognizer:shouldRequireFailureOfGestureRecognizer:

    这个方法返回YES,第一个则失效

    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
    shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
    

    顾名思义吧、Failure Of GestureRecognizer

    • - gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:

    这个方法返回YES,第二个手势则失败

    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
    shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
    

    顾名思义吧、Fail By GestureRecognizer

    需要注意这里

    些方法都有两个UIGestureRecognizer参数、所以在一个对象的代理中返回并不一定能起到决定性作用
    gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:举例:
    只要任意一个返回YES、则这两个就可以同时识别;
    只有两个都返回NO的时候、才是互斥的。


    UIGestureRecognizer

    • 手势识别流程

    大致理解是,Window在将事件传递给hit-tested view之前,会先将事件传递给相关的手势识别器并由手势识别器优先识别。若手势识别器成功识别了事件,就会取消hit-tested view对事件的响应;若手势识别器没能识别事件,hit-tested view才完全接手事件的响应权。

    佐证的话、你可以自定义一个子类并且重载一些方法、这里直接贴结果
    先用一个离散型手势做实验:

    14:20:17-[KTUITapGestureRecognizer touchesBegan:withEvent:]
    14:20:17-[KTUITapGestureRecognizer2 touchesBegan:withEvent:]
    14:20:17-[View2 touchesBegan:withEvent:]
    14:20:17-[View touchesBegan:withEvent:]
    14:20:18-[KTUITapGestureRecognizer touchesEnded:withEvent:]
    14:20:18-[KTUITapGestureRecognizer2 touchesEnded:withEvent:]
    14:20:18-[View2 tapAction]
    14:20:18-[View2 touchesCancelled:withEvent:]
    14:20:18-[View touchesCancelled:withEvent:]
    
    而对于持续型手势

    在一开始滑动的过程中,手势识别器处在识别手势阶段,滑动产生的连续事件既会传递给手势识别器又会传递给View,因此ViewtouchesMoved:withEvent:在开始一段时间内会持续调用;
    当手势识别器成功识别了该滑动手势时,手势识别器的action开始调用,同时通知Application取消View对事件的响应。之后仅由滑动手势识别器接收事件并响应,View不再接收事件。
    (其实原理都是一样的、只是持续性手势需要一个识别的过程而已)

    这里有几个点可以说说:

    1. 可以看到右侧有一秒的时间差
      也就是说View的后续动作会等待GestureRecognizer的识别结果。
    2. KTUITapGestureRecognizer以及KTUITapGestureRecognizer2的方法都被触发了
      也就是说是hit-tested列表中所有View上的手势识别器都会得到机会去识别事件。(依赖UITouch中的gestureRecognizers属性)
      至于最后触发谁、取决于代理中的设置。默认按照响应链的顺序。

    并且手势在触摸事件处理流程中,处于观察者的角色,其不是view层级结构的一部分,所以也不参与或者依赖responder chain(你把上层View的touch不调用super也影响不了)。

    其流程大概如下图所示:

    注:图中view与手势的关系是,手势关联在view或view的superview(可能多级)上。

    • 手势的识别并不依赖响应链
    • [KTUITapGestureRecognizer touchesBegan:withEvent:]堆栈信息:
    -[KTUITapGestureRecognizer touchesBegan:withEvent:](self=0x00006000003d6700, _cmd="touchesBegan:withEvent:", 0x0000600000dd1d40) ITapGestureRecognizer.m:14
    -[UIGestureRecognizer _touchesBegan:withEvent:] + 240
    -[UITouchesEvent _sendEventToGestureRecognizer:] + 287
    -[UIGestureEnvironment _updateForEvent:window:]_block_invoke + 64
    -[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:] + 276
    -[UIGestureEnvironment _updateForEvent:window:] + 200
    -[UIWindow sendEvent:] + 4058
    -[UIApplication sendEvent:] + 352
    

    可见手势的识别(KTUITapGestureRecognizertouchesBeagn)并不依赖响应链(ViewtouchesBeagn)、而是由[UIWindow sendEvent:]方法中被UIGestureEnvironment直接调起。

    • 6个子类手势

    UIGestureRecognizer为一个抽象基类,定义了实现底层手势识别行为的编程接口,你不应该直接使用他。

    • "离散手势"和"连续手势"

    手势分为离散型手势(discrete gestures)和持续型手势(continuous gesture)

    系统提供的离散型手势包括点按手势(UITapGestureRecognizer)和轻扫手势(UISwipeGestureRecognizer),其余均为持续型手势。

    两者主要区别在于状态变化过程:

    离散型:
    识别成功:Possible —> Recognized
    识别失败:Possible —> Failed

    持续型:
    完整识别:Possible —> Began —> [Changed] —> Ended
    不完整识别:Possible —> Began —> [Changed] —> Cancel

    • Action

    最多含有1个参数、这与UIControl不同

    - (void)handleGesture;
    - (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer;
    

    你可以向gestureRecognizer询问一些东西、比如可以通过调用locationInView:locationOfTouch:inView:来询问手势的位置。


    初始化

    • - initWithTarget:action:

    通过target&&action的形式初始化一个手势识别器

    - (instancetype)initWithTarget:(id)target 
                            action:(SEL)action;
    

    添加和移除Target&&Action

    • - addTarget:action:

    添加一对Target&&Action

    - (void)addTarget:(id)target 
               action:(SEL)action;
    

    与UIControl的机制一样、Target&&Action成对作为标识、再次添加无效。

    • - removeTarget:action

    移除一对Target&&Action

    - (void)removeTarget:(id)target 
                  action:(SEL)action;
    

    传递nil则匹配所有动作:
    Target=nil则删除所有
    Action=nil则删除该Target所有


    获取手势的触摸和位置

    • - locationInView:

    获取手势触摸的位置

    - (CGPoint)locationInView:(UIView *)view;
    

    如果传入nil则指定为window

    比如我们可以设定允许判定手势的rect

    //设置点击的范围
    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
    //获取当前的触摸点
      CGPoint curp = [touch locationInView:self.imageView];
      if (curp.x <= self.imageView.bounds.size.width*0.5) {
          return NO;
      }else{
    
          return YES;
      }
    }
    
    • - locationOfTouch:inView:

    多点触摸时、查找指定touch的poinit

    - (CGPoint)locationOfTouch:(NSUInteger)touchIndex 
                        inView:(UIView *)view;
    

    touchIndex代表指定touch的索引
    view传入nil则指定为window

    • numberOfTouches

    该手势所获取到的总触摸点数

    @property(nonatomic, readonly) NSUInteger numberOfTouches;
    

    获取手势的状态和View

    • state

    手势识别器当前的识别状态

    @property(nonatomic, readwrite) UIGestureRecognizerState state;
    

    UIGestureRecognizerState是一个枚举类型

    typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {
        //尚未识别是何种手势操作(但可能已经触发了触摸事件),默认状态
        UIGestureRecognizerStatePossible,   
        //手势已经开始,此时已经被识别,但是这个过程中可能发生变化,手势操作尚未完成
        UIGestureRecognizerStateBegan,     
        //手势状态发生改变
        UIGestureRecognizerStateChanged, 
        // 手势识别操作完成(此时已经松开手指)  
        UIGestureRecognizerStateEnded, 
        //手势被取消,恢复到默认状态   
        UIGestureRecognizerStateCancelled, 
        //手势识别失败,恢复到默认状态
        UIGestureRecognizerStateFailed,    
        //手势识别完成,同end
        UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded 
    };
    

    手势分为离散型手势(discrete gestures)和持续型手势(continuous gesture)
    二者可能经历的状态不一样。

    具体可以返回去看看《"离散手势"和"连续手势"》那一块的说明。

    • view

    手势所添加到的视图

    @property(nonatomic, readonly) UIView *view;
    
    • enabled

    手势识别器是否开启。默认YES

    @property(nonatomic, getter=isEnabled) BOOL enabled;
    

    如果在手势识别器正在识别手势时将此属性更改为NO,则手势识别器将转换为已取消状态。


    取消和延迟触摸

    • cancelsTouchesInView

    识别成功后--是否向View发送cancel消息。默认YES

    @property(nonatomic) BOOL cancelsTouchesInView;
    

    若设置成NO,表示手势识别成功后不取消响应链对事件的响应,事件依旧会传递给hit-test view。

    • delaysTouchesBegan

    手势识别器在识别手势期间,是否截断事件,即不会将事件发送给hit-tested view。默认为NO。

    @property(nonatomic) BOOL delaysTouchesBegan;
    

    正常情况

    14:20:17-[KTUITapGestureRecognizer touchesBegan:withEvent:]
    14:20:17-[KTUITapGestureRecognizer2 touchesBegan:withEvent:]
    14:20:17-[View2 touchesBegan:withEvent:]
    14:20:17-[View touchesBegan:withEvent:]
    14:20:18-[KTUITapGestureRecognizer touchesEnded:withEvent:]
    14:20:18-[KTUITapGestureRecognizer2 touchesEnded:withEvent:]
    14:20:18-[View2 tapAction]
    14:20:18-[View2 touchesCancelled:withEvent:]
    14:20:18-[View touchesCancelled:withEvent:]
    

    任意VIewtap.delaysTouchesBegan = YES;之后

    14:55:37-[KTUITapGestureRecognizer touchesBegan:withEvent:]
    14:55:37-[KTUITapGestureRecognizer2 touchesBegan:withEvent:]
    14:55:37-[KTUITapGestureRecognizer touchesEnded:withEvent:]
    14:55:37-[KTUITapGestureRecognizer2 touchesEnded:withEvent:]
    14:55:37-[View2 tapAction]
    

    可以看出来多个手势只要有一个设置为YES、整条响应链上的VIew都不会收到消息。

    • delaysTouchesEnded

    当手势识别失败时,若此时触摸已经结束,是否延迟调用响应者的 touchesEnded:withEvent。默认YES

    @property(nonatomic) BOOL delaysTouchesEnded;
    

    若设置成NO,则在手势识别失败时会立即通知Application发送状态为end的touch事件给hit-tested view以调用 touchesEnded:withEvent: 结束事件响应。
    若设置成YES,会延迟大概0.15ms,期间没有接收到别的touch才会发送touchesEnded。


    添加依赖

    • requireGestureRecognizerToFail:

    只有当另一个手势识别失败时才会除非本手势

    - (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;
    

    [singleTapGesture requireGestureRecognizerToFail:doubleTapGesture];
    这样即使singleTapGesture已经识别成功、也会等到doubleTapGesture识别失败再触发自身Action


    最后

    本文主要是自己的学习与总结。如果文内存在纰漏、万望留言斧正。如果愿意补充以及不吝赐教小弟会更加感激。


    参考资料

    官方文档-UIGestureRecognizer
    你真的了解UIGestureRecognizer吗?
    iOS-UIGestureRecognizer详解-原理篇
    UIGestureRecognizer学习笔记
    iOS触摸事件全家桶

    相关文章

      网友评论

        本文标题:iOS文档补完计划--UIGestureRecognizer

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