Event Responder

  • 当一个view被点击了,是通过什么样的机制知道是该view被点击的呢?
  • 如果父视图上存在两个视图,并且这两个视图都可以被点击,我们怎么选择性的跳过第一个视图,直接相应第二视图呢?(第一个视图盖在第二个视图上面,这种情况在开发中有时候也会遇到)

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

Event Responder_第1张图片
示意图.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)