事件响应链

283 阅读4分钟

1、当用户操作屏幕,IOKit收到屏幕操作,会将这次操作封装为IOHIDEvent对象。通过mach port将事件转发给SpringBoard来处理。

2、SpringBoard是iOS系统的桌面程序。SpringBoard收到mach port发过来的事件,唤醒main runloop来处理。

3、main runloop将事件交给source1处理,source1会调用__IOHIDEventSystemClientQueueCallback()函数。

函数内部会判断,是否有程序在前台显示,如果有则通过mach port将IOHIDEvent事件转发给这个程序。

如果前台没有程序在显示,则表明SpringBoard的桌面程序在前台显示,也就是用户在桌面进行了操作。

__IOHIDEventSystemClientQueueCallback()函数会将事件交给source0处理,source0会调用__UIApplicationHandleEventQueue()函数,函数内部会做具体的处理操作。

4、例如用户点击了某个应用程序的icon,会将这个程序启动。

应用程序接收到SpringBoard传来的消息,会唤醒main runloop并将这个消息交给source1处理,source1调用__IOHIDEventSystemClientQueueCallback()函数,在函数内部会将事件交给source0处理,并调用source0的__UIApplicationHandleEventQueue()函数。

在__UIApplicationHandleEventQueue()函数中,会将传递过来的IOHIDEvent转换为UIEvent对象。

5、在函数内部,将事件放入UIApplication的事件队列,等到处理该事件时,将该事件出队列,UIApplication将事件传递给窗口对象(UIWindow),如果存在多个窗口,则从后往前询问最上层显示的窗口

6、窗口UIWindow通过hitTest和pointInside操作,判断是否可以响应事件,如果窗口UIWindow不能响应事件,则将事件传递给其他窗口;若窗口能响应事件,则从后往前询问窗口的子视图。

7、以此类推,如果当前视图不能响应事件,则将事件传递给同级的上一个子视图;如果能响应,就从后往前遍历当前视图的子视图。

8、如果当前视图的子视图都不能响应事件,则当前视图就是第一响应者。

9、找到第一响应者,事件的传递的响应链也就确定的。

10、如果第一响应者非UIControl子类且响应链上也没有绑定手势识别器UIGestureRecognizer;

11、那么由于第一响应者具有处理事件的最高优先级,因此UIApplication会先将事件传递给它供其处理。首先,UIApplication将事件通过 sendEvent: 传递给事件所属的window,window同样通过 sendEvent: 再将事件传递给hit-tested view,即第一响应者,第一响应者具有对事件的完全处理权,默认对事件不进行处理,传递给下一个响应者(nextResponder);如果响应链上的对象一直没有处理该事件,则最后会交给UIApplication,如果UIApplication实现代理,会交给UIApplicationDelegate,如果UIApplicationDelegate没处理,则该事件会被丢弃。

12、如果第一响应者非UIControl子类但响应链上也绑定了手势识别器UIGestureRecognizer;

13、UIWindow会将事件先发送给响应链上绑定的手势识别器UIGestureRecognizer,再发送给第一响应者,如果手势识别器能成功识别事件,UIApplication默认会向第一响应者发送cancel响应事件的命令;如果手势识别器未能识别手势,而此时触摸并未结束,则停止向手势识别器发送事件,仅向第一响应者发送事件。如果手势识别器未能识别手势,且此时触摸已经结束,则向第一响应者发送end状态的touch事件,以停止对事件的响应。

14、如果第一响应者是自定义的UIControl的子类同时响应链上也绑定了手势识别器UIGestureRecognizer。这种情况跟第一响应者非UIControl子类但响应链上也绑定了手势识别器UIGestureRecognizer处理逻辑一样;

15、如果第一响应者是UIControl的子类且是系统类(UIButton、UISwitch),同时响应链上也绑定了手势识别器UIGestureRecognizer。

16、UIWindow会将事件先发送给响应链上绑定的手势识别器UIGestureRecognizer,再发送给第一响应者,如果第一响应者能响应事件,系统会判断其是继承自UIControl还是UIResponder,如果是继承自UIControl,则直接通过UIApplication直接向其派发消息,并且不再向响应者链派发消息。