Event Responder

作者: 董二千 | 来源:发表于2016-02-26 19:58 被阅读39次
    • 当一个view被点击了,是通过什么样的机制知道是该view被点击的呢?
    • 如果父视图上存在两个视图,并且这两个视图都可以被点击,我们怎么选择性的跳过第一个视图,直接相应第二视图呢?(第一个视图盖在第二个视图上面,这种情况在开发中有时候也会遇到)

    当然我们iOS设备的屏幕,UIKit就会生成一个事件对象UIEvent。UIApplication获取到UIEvent之后,会通过hitTest方法来决定把UIEvent传递给谁。hitTest的作用就是找出触摸点下面的view是什么(first Responder)。

    示意图.png

    假设点击了 C-3,流程如下。
    1.因为触摸点在A里面,检查subviews,B-1和B-2。
    2.因为触摸点在B-2,里面检查B-2的subviews,C-2和C-3。
    3.因为触摸点在C-3里面,检查C-3的subviews,没有,返回C-3。这时候C-3就是hitTest View,就是第一响应者。

    以上步骤回答了问题1

    Tips:

    • hitTest的顺序是从A-B-C,而不是从第一个点击的view开始。这个很容易误会。
    • 如果点击没有发生在B-1中,那么也不会检测C-1。
    • 如果你的Subview设置了clipsToBounds=NO,实际显示区域可能超出了superView的frame,你点击超出的部分,是不会处理你的事件的。

    UIView提供两个方法来确定Firstr Responder

    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; // recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
    
    - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event; // default returns YES if point is in bounds
    
    • 当一个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; 
                  }
    }
    

    通过上面的方法可以控制谁是First Responder,回答了问题二。

    hit-Test 是事件分发的第一步,就算你的app忽略了事件,也会发生hit-Test。确定了hit-TestView之后,才会开始进行下一步的事件分发

    iOS事件分发机制(一) hit-Testing
    iOS事件分发机制(二)The Responder Chain

    相关文章

      网友评论

        本文标题:Event Responder

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