当手指触摸到屏幕时, 一个触摸事件就在系统中产生了.通过进程间通信(IPC), 最终传递给合适的应用,并找到应用中的最佳响应者进行响应. 整个流程大概如下:
事件响应链.png
1. 系统响应阶段
- 当手指触摸到屏幕, 屏幕感受到触摸后, 系统会将触摸交给
IOKit.framework处理.IOKit.framework在内部会将触摸封装成一个IOHIDEvent对象. 并且通过mach port递交给SpringBoard.app(系统桌面)进程
mach port 进程端口,各进程之间通过它进行通信。
SpringBoad.app 是一个系统进程,可以理解为桌面系统,可以统一管理和分发系统接收到的触摸事件。
-
SpringBoard进程收到这个IOHIDEvent对象, 会触发SpringBoard .app进程中的主线程runloop的source1事件回调. 此时SpringBoard .app会根据桌面状态, 判断是否有运行在前台的进程. 若无app在前台运行, 那么会触发SpringBoard .app进程主线程runloop的source0回调, 事件由SpringBoard进程内部消耗. 若有app在前台运行, 则SpringBoard通过mach port将IOHIDEvent分发给该app. 接下来就是该app内部消耗这个IOHIDEvent对象的过程. -
app接受到这个IOHIDEvent对象, 其主线程的runloop被唤醒, 其中的source1回调被触发, 并触发source1中的source0回调, 在这个source0回调中,IOHIDEvent对象被封装成为一个UIEvent对象. 此时app正式开始对事件的响应. -
source0回调将这个UIEvent对象添加到给UIApplication对象的事件队列中. 事件出队后,UIApplication开始一系列寻找最佳响应者的过程, 称为Hit-Testing.
2. Hit-Testing
- 从逻辑上来讲,
探测链机制是最先发生的. 当产生触摸事件后, iOS系统会通过Hit-Testing来找到最佳响应者, 去响应这个触摸事件. 只要用到了UIView的两个方法:
// recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
// default returns YES if point is in bounds
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;
前者会通过recursively calls递归调用来返回一个适合响应触摸事件的UIView对象:
hitTest
如果通过demo来验证
Hit-Testing, 会发现, 其实整个Hit-Testing过程会发生两次. 对此苹果官方有相应的回复:Yes, it’s normal. The system may tweak the point being hit tested between the calls. Since hitTest should be a pure function with no side-effects, this should be fine.
-
UIApplication通过Hit-Testing找到了最佳响应者并且遍历得到其所有的UIGestureRecognizer, 然后根据最佳响应者、UIGestureRecognizer、UIWindow创建UITouches对象, 并将其保存在UIEvent中. -
UIApplication将UIEvent发送给UIWindow,UIWindow首先将事件发送给UIGestureRecognizer, 然后发送给最佳响应者, 事件沿Responder Chain传递. -
UIGestureRecognizer开始识别自己的gesture, 当某个gestureRecognizer识别成功后,UIGestureRecognizer将独占所有需要的UITouches, 识别成功的gesture所关联的UITouch中的hitTest view收到-touchesCancelled()消息, 并且此后该hitTest view不再收到该UITouch事件 -
target: action:触发,action方法内部实现相应的触摸事件响应.事件处理完成, 若runloop无其他事件处理, 则runloop进入休眠, 等待下一个事件到来. -
识别成功的
gestureRecognizer所关联的UITouches的其他所有gestureRecognizer收到-touchesCancelled()消息, 此后不再收到该UITouch事件. -
如果没有识别成功的
gestureRecognizer, 那么hitTest view将不会收到-touchesCancelled()消息. 若有最佳响应者能够处理该事件. 那么事件处理完成, 若runloop无其他事件处理, 则runloop进入休眠, 等待下一个事件到来. -
若
最佳响应者没能够处理该事件, 则进行下个步骤:Responder Chain
3. Responder Chain
通过Hit-Testing找到的视图拥有最先对触摸事件进行响应的机会. 如果这个视图无法处理触摸事件, 就会沿着Responder Chain向上进行传递.直到找到可以处理事件的视图, 或者一直到UIApplication都没有能够处理, 该事件就会被废弃掉. 若runloop无其他事件, 则进入休眠, 等待下一个事件到来.











网友评论