iOS 事件响应链 & 事件传递链
1. 事件传递链(Event Delivery Chain)
概念
当用户触摸屏幕时,事件首先由 系统(UIApplication) 接收,然后按以下顺序传递:
- UIApplication → 2. UIWindow → 3. 根视图控制器(Root ViewController) → 4. 视图层级(UIView)
事件传递流程
当手指触摸屏幕时:
- UIApplication 接收到事件,将其交给当前的 keyWindow(UIWindow) 处理。
- UIWindow 调用
hitTest(_:with:)
方法,找到触摸点所在的 最底层视图(最精确的 View)。 - UIView 层级递归执行
hitTest(_:with:)
,最终返回触摸点所在的 最前面的子视图(即最符合触摸点的视图)。 - 如果 找到合适的 View ,事件将传递给这个 View 的
touchesBegan(_:with:)
方法,否则事件被丢弃。
事件传递代码示例
swift
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// 检查自己是否能接收事件
if !self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01 {
return nil
}
// 检查触摸点是否在当前 View 内
if self.point(inside: point, with: event) {
// 倒序遍历子视图,找到最前面的 View
for subview in self.subviews.reversed() {
let convertedPoint = subview.convert(point, from: self)
if let hitView = subview.hitTest(convertedPoint, with: event) {
return hitView
}
}
return self // 如果没有找到子视图,则自己接收事件
}
return nil
}
2. 事件响应链(Responder Chain)
概念
当 事件传递链 找到目标 UIView 后,事件需要进行 响应处理 。如果该视图无法处理,事件会沿着 响应者链(Responder Chain) 向上传递,直到被某个响应者处理或最终被丢弃。
事件响应链传递路径
当 UIView
不能处理事件时,事件会沿着以下路径 向上传递:
- 当前 View(UIView)
- View 的父视图(superview)
- View 所在的 ViewController(UIViewController)
- ViewController 所属的 UIWindow
- UIApplication(全局应用对象)
- 如果仍未处理,则被丢弃
事件响应链代码示例
swift
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("\(self) 处理触摸事件")
super.touchesBegan(touches, with: event) // 传递给下一个响应者
}
如果当前 View 没有实现 touchesBegan(_:with:)
,则会调用 nextResponder()
方法,将事件传递给 下一个响应者。
如何获取 nextResponder()
swift
override var next: UIResponder? {
return self.superview // 下一个响应者是父视图
}
3. 事件传递链 vs 响应链
事件传递链(Event Delivery) | 事件响应链(Responder Chain) | |
---|---|---|
作用 | 负责找到 最适合的 View 处理事件 | 负责 事件处理,如果无法处理则向上传递 |
传递方向 | 从 UIApplication → UIWindow → 视图层级 | 从 当前 View 向上 → 父视图 → 控制器 → UIWindow → UIApplication |
核心方法 | hitTest(_:with:) 、pointInside(_:with:) |
touchesBegan(_:with:) 、nextResponder() |
终点 | 找到 最前面的 View | 找到 能够处理事件的响应者 |
4. 事件传递 & 响应示例
示例:自定义按钮 MyButton
传递 & 响应事件
swift
class MyButton: UIButton {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
print("检查是否可以接收事件: \(self)")
return super.hitTest(point, with: event)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("MyButton 处理触摸事件")
super.touchesBegan(touches, with: event)
}
}
如果 MyButton
无法处理事件 ,会沿着 响应链向上传递 ,直到 UIViewController
处理它。
5. 结论
- 事件传递链 :确定由 哪个 View 处理事件 (
hitTest(_:with:)
)。 - 事件响应链 :确定 如何处理事件 ,如果当前 View 无法处理,则 沿着响应链传递 (
nextResponder()
)。 - 事件最终要么被处理,要么被丢弃。
这就是 iOS 事件传递和响应机制 ,掌握这个知识点有助于 处理自定义手势、拦截触摸事件、调整事件传递逻辑!🚀