前言
最近有两句话让我颇有感触:
人往往会高估短期效果,低估了长期主义的力量
知识是相互连接,与旧有知识连接越多,形成一棵属于你自己的知识树,你就记得越牢
在日??⒅?,对碰到的知识点总是希望能一下子吃透吃精,而欠缺总结、连接、长期坚持的韧劲,你学习到的知识点永远是孤立的。一两个月过去,你可能发现肚子里空空如也,当时看的东西全都已经不记得了。
写这个问答系列的目的有两个:
- 系统梳理:输出倒逼输入,系统梳理每个比较重要的 iOS 问题,而不只是简简单单看过一遍就好了
- 建立每个问答知识点的连接:长成属于自己的知识树
正文开始
第一阶段:IOKit
当一个硬件事件发生后,首先由 IOKit.framework 生成 IOHIDEvent,并会通过 mach port(进程间通信)传递给 SpringBoard。
第二阶段:SpringBoard
SpringBoard 是一个系统进程,可理解为 iOS 的桌面系统,统一管理和分发系统接收到的触摸事件。
如果是在桌面操作,将交由系统进程去消耗。如果是在 APP 内部操作, SpringBoard 将触摸事件 IOHIDEvent 通过 mach port (进程间通信),传递给 APP 进程。
第三阶段:APP 主线程 RunLoop Source1 回调
App 主线程 RunLoop 的 Source1 触发回调,接下来 Source1 会触发 Source0 回调
Source1:是基于 mach port 的,用于监听系统内核或者其他进程或线程的事件。
第四阶段:APP 主线程 RunLoop Source0 回调
触发 Source0 (非基于 port)回调,将 IOHIDEvent 封装成 UIEvent 对象,并调用 UIApplication 的 sendEvent 方法。
比如在 UIView 中 hitTest 方法中打断点,在控制台中输入 po [NSThread callStackSymbols]
,查看完整堆栈信息,我们可以看到 Source0 的身影。
第五阶段:自上往下寻找最佳响应者
由 UIApplication 为起点,开始寻找最佳响应者
- 首先,系统通过 UIApplication -> UIWindow -> UIViewController -> UIView 顺序查找
- 到达 UIView 后,系统通过 hitTest 方法,按照视图层级数自底向上(从根视图到子视图),从前向后(同一层级视图,优先最上层)依次进行遍历,最终找到一个最佳响应者
注:当 UIView 处于以下任一状态时,不能接收触摸事件
userInteractionEnabled = NO
hidden = YES
alpha < 0.01
第六阶段:自底向上事件响应
从最佳响应者开始,调用下列方法,并沿着响应链向上传递事件。
touchBegan、touchMoved、touchEnded、touchCancelled
响应的一些原则:
- 响应链中的 UIResponder 默认会向上传递响应事件
- 当 UIResponder 重写了 touchBegan 等方法,并且没有调用下一个 UIResponder 对应方法时,响应事件将停止向上传递
- UIResponder 有一个特殊的子类 UIControl,默认会阻断事件向上传递
- 响应事件最高将传到顶层 UIApplication,UIApplication 不做任何处理,并抛弃该事件
事件响应完毕,主线程的 RunLoop 开始睡眠,等待下一个事件到来。
其它:存在 UIGestureRecognizer 的情况
上面说完了触摸事件的主流程,现在我们再来说说存在 UIGestureRecognizer 时,自底向上事件响应的变化。
- 当 View 上存在 UIGestureRecognizer 时,从最佳响应者开始,UIGestureRecognizer 优先收到触摸事件(touchBegan 调用),View 延迟收到(touchBegan 调用)。
- 当 UIGestureRecognizer 识别成功后,会独占所有相关的 UITouch,UIGestureEnvironment 会发起响应链的 cancel,调用 UIResponder touchesCancelled 方法,并且此后 View 或者其它 gesture,都将不再收到此类事件。UIGestureRecognizer touch 类的方法仍旧向上传递
注1:UIGestureRecognizer 对应于 UIResponder,同样拥有配套的 touchBegan、touchMoved、touchEnded、touchCancelled。
注2:将 UIGestureRecognizer 的 cancelsTouchesInView 设置为 false,即使手势被识别,仍旧不会调用 touchCancelled 方法
- UIGestureRecognizer 对应的 target action 方法被调用,完成事件响应。
iOS 中手势包括
- 点击,可以是单击、双击 UITapGestureRecognizer
- 捏(对角线缩小放大) UIPinchGestureRecognizer
- 拖拽 UIPanGestureRecognizer
- 滑动(左滑,上滑,下滑等) UISwipeGestureRecognizer
- 旋转 UIRotationGestureRecognizer
- 长按 UILongPressGestureRecognizer
整体流程图
关联的知识点
- iOS 系统底层介绍
- iOS 进程间通信
- RunLoop 流程