美文网首页
iOS事件分发机制之 hit-Testing

iOS事件分发机制之 hit-Testing

作者: 南华coder | 来源:发表于2017-01-06 19:07 被阅读264次

    这里就解析一个事情:iOS是如何找到处理触摸事件的视图

    <h5>关键词:</h5>
    ** Hit-Test View
    The lowest view in the view hierarchy that contains the touch point becomes the hit-test view,我的理解是: 当你点击了屏幕上的某个view,这个动作由硬件层传导到操作系统,然后又从底层封装成一个事件(Event)顺着view的层级往上传导,一直要找到含有这个点击点
    层级最低(逻辑上最靠近手指)的view来响应事件,这个view就是hit-test view**。

    决定谁是hit-test view是通过不断递归调用view中的 *- (UIView )hitTest: withEvent: 方法和 -(BOOL)pointInside: withEvent: 方法来实现的

    ** hit-Testing**:找出这个触摸点下面的hit-test view的过程,HitTest会检测这个点击的点是不是发生在这个View上,如果是的话,就会去遍历这个View的subviews,直到找到最小的能够处理事件的view,如果整了一圈没找到能够处理的view,则返回自身。

    UIView中提供两个方法用来确定hit-test View
    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
    //该方法的处理过程:
    //首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内;
    //YES在,则遍历当前视图的所有子视图(subviews),调用子视图的hitTest:withEvent:;
    // NO不在,当前视图的hitTest:withEvent:返回nil
    //若第一次有子视图的hitTest:withEvent:方法返回非空对象,则当前视图的hitTest:withEvent:方法就返回此对象,处理结束
    //若所有子视图的hitTest:withEvent:方法都返回nil,则当前视图的hitTest:withEvent:方法返回当前视图自身(self)

    //判断触摸点是否在当前视图内,可以用来实现扩大View的相应区域
    - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
    

    hit-Testing的检查机制

    1、默认的hit-testing顺序是按照UIView中Subviews的逆顺序;
    2、如果View的同级别Subview中有重叠的部分,则优先检查顶部的Subview,如果顶部的Subview返回nil, 再检查底部的Subview;
    3、如果点击没有发生在某View中,那么该事件就不可能发生在View的Subview中,所以检测过程中发现该事件不在ViewB内,也直接就不会检测在不在ViewF内。也就是说,如果你的Subview设置了clipsToBounds=NO,实际显示区域可能超出了superView的frame,你点击超出的部分,是不会处理你的事件的。

    简言之
    当一个View收到hitTest消息时,会调用自己的pointInside:withEvent:方法,如果pointInside返回YES,则表明触摸事件发生在我自己内部,则会遍历自己的所有Subview去寻找最小单位(没有任何子view)的UIView,如果当前View.userInteractionEnabled = NO,enabled=NO(UIControl),或者alpha<=0.01, hidden等情况的时候,hitTest就不会调用自己的pointInside了,直接返回nil,然后系统就回去遍历兄弟节点。

    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
       if (self.alpha <= 0.01 || !self.userInteractionEnabled || self.hidden) { 
          return nil; 
       } 
       BOOL inside = [self pointInside:point withEvent:event]; 
       UIView *hitView = nil; 
       if (inside) { 
          NSEnumerator *enumerator = [self.subviews reverseObjectEnumerator]; 
          for (UIView *subview in enumerator) { 
              hitView = [subview hitTest:point withEvent:event]; 
              if (hitView) { 
                  break; 
              } 
          } 
          if (!hitView) { 
            hitView = self; 
          } 
          return hitView; 
      }else{ 
          return nil; 
      }
    }
    

    当确定了Hit-TestView时,如果当前的application没有忽略触摸事件 (UIApplication:isIgnoringInteractionEvents),则application就会去分发事件(sendEvent:->keywindow:sendEvent:)

    hitTest@2x.png

    上图找到D的过程为例:A -> C ->E -> D
    后续:当我们确定了hit-Test View之后,事件分发就正式开始了,如果hitTestView可以直接处理的,就处理,不能处理的,则交给 The Responder Chain/ GestureRecognizer。详情见iOS的响应链

    遗留问题:
    iOS事件响应链中Hit-Test View的应用ScrollView page滑动

    iOS事件分发机制(一) hit-Testing
    hitTest和pointInside如何响应用户点击事件
    iOS事件响应链中Hit-Test View的应用

    相关文章

      网友评论

          本文标题:iOS事件分发机制之 hit-Testing

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