iOS的事件传递与响应者链

856 阅读3分钟

这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战

事件响应链

包括点击事件,画面刷新事件等。在视图栈内从上至下,或者从下至上传播。可以说点事件的的分发,传递以及处理。

事件的产生和传递过程:

    1. 当触摸事件发生时,压力转为电信号,iOS系统将产生UIEvent对象,记录事件产生的事件和类型,然后系统将事件加入到一个由UIApplication管理的事件队列中。
    1. UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常会先发送事件给应用程序的主窗口(keyWindow
    1. 主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件
    1. 找到合适的视图控件后,就会调用视图控件的touches方法来作事件的具体处理:touchesBegin··· touchesMoved··· touchesEnded···等
    1. 这些touches方法默认的做法是将事件顺着响应链条向上传递,将事件交给上一个响应者处理 一般事件的传递是从父控件传递到子控件的,如果父控件接受不到触摸事件,那么子控件就不可能接收到触摸事件。
      UIView不能接收触摸事件的三种情况:
    1. 不接受用户交互:userInteractionEnabled = NO
    1. 隐藏: hidden= YES
    1. 透明: alpha = 0.0 ~0.01 用户的触摸事件首先会由系统截获,进行包装处理等。
      然后递归遍历所有的view,进行碰触测试(hitTest),直到找到可以处理事件的view。
- (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

大致的过程 application -> window -> root view -> ······ -> lowest view

响应者链

响应者链其实就是很多响应者对象(继承自UIResponder的对象)一起组合起来的链条称之为响应者链条。

一般默认做法是控件将事件顺着响应者链条向上传递,将事件交给上一个响应者进行处理。那么如何判断当前响应者的上一个响应者是谁呢?有以下两个规则:

    1. 判断当前是否是控制器的View,如果是控制器的View,上一个响应者就是控制器
    1. 如果不是控制器的View,上一个响应者就是父控件 当有view能够处理触摸事件后,开始响应事件。系统会调用view的以下方法:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;

可以多对象共同响应事件。只需要在以上方法重载中调用super的方法。
大致的过程initial view -> super view -> ···..->view controller -> window -> Application

需要特别注意的一点是,传递链中是没有controller的,因为controller本身不具有大小的概念。但是响应链中是有controller的,因为controller继承自UIResponder

UIApplication -> UIWindow -> 递归找到最合适处理的控件 -> 控件调用touches方法 -> 判断是否实现touches方法 -> 没有实现默认会将事件传递给上一个响应者 -> 找到上一个响应者 -> 找不到方法作废

PS:利用响应者链条我们可以通过touches 的super方法,让多个响应者同时响应该事件。