复习-响应链与事件传递

178 阅读2分钟

名词与基本概念

  1. 响应者:
    在iOS中,响应者为能响应事件的UIResponder子类对象,如UIButton、UIView等。

  2. 响应链:
    响应链是由链接在一起的响应者(UIResponse子类)组成的。默认情况下,响应链是由第一响应者,到application对象以及中间所有响应者一起组成的。

  3. 事件传递:
    获得响应链后,将事件由第一响应者往application传递的过程即为事件传递。

简单总结

事件的响应流程如下

1.首先通过 hitTest:withEvent: 确定第一响应者,以及相应的响应链(UIApplication -> UIWindow -> ... -> 响应者)
2.判断第一响应者能否响应事件,如果第一响应者能进行响应则事件在响应链中的传递终止。如果第一响应者不能响应则将事件传递给 nextResponder也就是通常的superview进行事件响应
3.如果事件继续上报至UIWindow并且无法响应,它将会把事件继续上报给UIApplication
4.如果事件继续上报至UIApplication并且也无法响应,它将会将事件上报给其Delegate
5.如果最终事件依旧未被响应则会被系统抛弃

一些实现

寻找响应者:UIApplication -> UIWindow -> ... -> 响应者

// 寻找响应者
- (UIView *)hitTest:(CGPoint)point 
          withEvent:(UIEvent *)event;
// 判定是否在view内
- (BOOL)pointInside:(CGPoint)point 
          withEvent:(UIEvent *)event;
// hit test 假代码
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if (self.hidden || !self.userInteractionEnabled || self.alpha < 0.01 || ![self pointInside:point withEvent:event] || ![self _isAnimatedUserInteractionEnabled]) {
        return nil;
    } else {
        for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
            UIView *hitView = [subview hitTest:[subview convertPoint:point fromView:self] withEvent:event];
            if (hitView) {
                return hitView;
            }
        }
        return self;
    }
}
// UIApplication 派发event的两个方法,派发给合适的响应者。 (对于响应链来说是他的windows)
- (void)sendEvent:(UIEvent *)event;
- (BOOL)sendAction:(SEL)action 
                to:(id)target 
              from:(id)sender 
          forEvent:(UIEvent *)event;
// Dispatches the specified event to its views. UIWindow派发事件给他的 views
- (void)sendEvent:(UIEvent *)event;

事件传递: 响应者 -> super view -> ... -> UIApplication
看谁能实现

几个要点

1. 手势比响应链拥有更高的优先级
2. 视图不响应检查要点

hidden = YES 视图被隐藏
userInteractionEnabled = NO 不接受响应事件
alpha <= 0.01,透明视图不接收响应事件
子视图超出父视图范围
需响应视图被其他视图盖住
是否重写了其父视图以及自身的hitTest方法
是否重写了其父视图以及自身的pointInside方法