iOS 事件类型
1 | typedef NS_ENUM(NSInteger, UIEventType) { |
iOS 中主要有以上几种事件,本文主要探讨触摸事件的产生、分发、响应过程。
整个iOS 触摸事件的产生到完成(销毁)的过程如下图:
事件的产生
- 当用户手指触摸屏幕时,屏幕硬件会将感应到的事件传递给IOKit,IOKit封装触摸事件为
IOHIDEvent
对象,通过mach port
(IPC进程间通信) 将事件发送给SpringBoard.app
。SpringBoard.app
主线程runloop被IOKit.framework转发的消息唤醒,触发Source1
回调__IOHIDEventSystemClientQueueCallback()
。 - SpringBoard.app 检测到有app在前台,通过mach port(IPC)转发给app。
- 前台app 主线程的Runloop被
Springboard
转发来的消息唤醒,并触发Source1
回调__IOHIDEventSystemClientQueueCallback()
,该方法内部会触发Source0
回调__UIApplicationHandleEventQueue()
,Source0
回调内部封装IOHIDEvent
为UIEvent
,Source0
回调内部调用UIApplication
的sendEvent:
方法。
此时一个UIEvent
事件产生了,开始进入事件传递流程。
事件传递机制
UIApplication
通过sendEvent:
方法将事件传递给UIWindow,UIWindow递归调用UIView层级的hitTest:withEvent:
方法,找到事件的第一响应者,并将该view返回。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
if !self.userInteractionEnabled || self.hidden || self.alpha <= 0.01 {
return nil
}
if self.pointInside(point, withEvent: event) {
for view: UIView in self.subviews {
let covertPoint = view.convertPoint(point, fromView: self)
let hitTestView: UIView? = view.hitTest(covertPoint, withEvent: event)
if hitTestView != nil {
return hitTestView
}
}
return self
}
return nil
}
响应链
能响应事件的都是UIResponder
及其子类,每个UIResponder
都有nextResponder
属性。UIWindow将事件分发给第一响应者后,如果第一响应者没有实现以下方法,那么就会将该事件交给nextResponder处理。如果沿着响应链没有发现能够响应事件的响应者,那么这个事件就会被忽略。
由此可见事件传递是由上向下传递,响应链是由下向上传递的。1
2
3
4public func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
public func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
public func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?)
public func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?)
UIGestureRecognizer & UIControl 响应事件过程
UIGestureRecognizer
在寻找第一响应者者的过程中,如果view绑定了UIGestureRecognizer
,会将view的Recognizer
添加进UITouch
的gestureRecognizers
数组中,UIWindow
会先将事件发送给Recognizer
,成功识别事件的Recognizer
所关联的UIView会收到touchesCancelled:
消息并且以后改view不会再收到这个UIEvent消息。如果Recognizer
没有响应,事件将会传递给第一响应者,并沿响应链向上传递。
UIControl
如果第一响应者是UIControl
的子类,非系统级的UIControl
类如UIButton
,则优先响应UIGestureRecognizer
,如果UIButton
是第一响应者,则直接由UIApplication
派发事件,调用sendEvent:forAction:
。
参考
深入浅出iOS事件机制
iOS触摸事件的流动
UIGestureRecognizer学习笔记
OS触控响应中那些没有细想过的问题
IOHIDFamily
iOS 事件处理机制与图像渲染过程