iOS-详解事件传递和响应者链

事件响应

一、事件的分发和传递(确定事件的第一响应者):

1.当iOS程序中发生触摸事件后,系统会将事件加入到UIApplication管理的一个任务队列中
2.UIApplication将处于任务队列最前端的事件向下分发。即UIWindow。
3.UIWindow将事件向下分发,即UIView。
4.UIView首先看自己是否能处理事件,触摸点是否在自己身上。如果能,那么继续寻找子视图。
5.遍历子控件,重复以上两步。
6.如果没有找到,那么自己就是事件处理者。如果
7.如果自己不能处理,那么不做任何处理。
其中 UIView不接受事件处理的情况主要有以下三种
1)alpha <0.01
2)userInteractionEnabled = NO(中间一层userInteractionEnabled = NO,则事件响应者链受阻)
3.hidden = YES.
第4步中UIView如何判断触摸点在自己身上呢?

-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    return [super pointInside:point withEvent:event];
}

return YES,则点在自己身上,遍历子视图寻找下一层目标View,return NO,则此view和此view 的子view都不响应事件,不会成为事件的第一响应者。

遍历子视图,寻找下一层目标view:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    // 1.如果控件不允许与用用户交互,那么返回nil
    if (self.userInteractionEnabled == NO || self.alpha <= 0.01 || self.hidden == YES){
        return nil;
    }
    // 2. 如果点击的点在不在当前控件中,返回nil
    if (![self pointInside:point withEvent:event]){
        return nil;
    }
    // 3.从后往前遍历每一个子控件
    for(int i = (int)self.subviews.count - 1 ; i >= 0 ;i--){
        // 3.1获取一个子控件
        UIView *childView = self.subviews[i];
        // 3.2当前触摸点的坐标转换为相对于子控件触摸点的坐标
        CGPoint childP = [self convertPoint:point toView:childView];
        // 3.3判断是否在在子控件中找到了更合适的子控件(递归循环)
        UIView *fitView = [childView hitTest:childP withEvent:event];
        // 3.4如果找到了就返回
        if (fitView) {
            return fitView;
        }
    }
    // 4.没找到,表示没有比自己更合适的view,返回自己
    return self;
}

子view会重复以上两个方法,最后的一个响应者即为第一响应者。

二、确定响应者后进入事件处理流程:

第一响应者无响应且无任何操作,则事件想下一个响应者传递,都没处理,则事件废弃
第一响应者响应事件,事件处理完成
如下图点击屏幕中的view2,则
AppDelegate --> window --> rootViewController --> contentView --> view2
所组成的链条就是事件响应者链。
整个过程详见下图1:
注意一点:点击屏幕后不是点击了屏幕中的view,而仅仅是点击了屏幕,然后生成了一个event包含了点击坐标的信息,iOS系统会接收到点击事件交给当前前台的UIApplication继续往下处理,要是直接点击到view了还要什么响应者链

视图层级

图1
改变响应者链

某个事件响应者链中View的pointInSide方法返回NO,则此view不响应事件,此view的上一响应者即为第一响应者。由下图2可看出响应链变小,此时点击view1、view2、contentView的第一响应者都是rootViewController。

图2

手动更改某个view的nextResponder也会改变事件响应者链,例如viewController的默认view的nextResponder指向了viewController,viewController上的View的点击事件全部ViewController中响应,类似图2中的contentView的点击事件全部在rootViewController(contentView的nextResponder)中响应

欢迎留言,共同进步。

参考资料:
1、iOS-事件传递,响应者链条及常见面试题:https://www.jianshu.com/p/0892b08367cf
2、iOS 响应者及响应者链:https://www.jianshu.com/p/89d20caabf41
3、UIView的hitTest和pointInside方法:https://www.jianshu.com/p/c87de31b3985
4、hitTest和pointInside方法你真的熟吗?:https://juejin.im/entry/59e07e616fb9a04528458048
5、iOS事件响应链(通俗易懂篇):https://blog.csdn.net/u013602835/article/details/79986819

你可能感兴趣的:(iOS-详解事件传递和响应者链)