UI事件传递
UIView
内部包含一个 hitTest
方法,用于 UI 事件的传递。
当一个触摸事件发生时,事件传递顺序如下:
UIApplication -> UIWindow -> 寻找合适的View
假设颜色对应的 View 关系如下:
黄色:View 1
绿色:View 1_1
灰色:View 1_1_1
红色:View 1_2
View 1
定义如下:
class View1: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
print("View 1 hitTest")
return super.hitTest(point, with: event)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("View 1 touchesBegan")
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
print("View 1 touchesMoved")
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
print("View 1 touchesCancelled")
}
}
同理,View11
、View111
、View12
定义类似于 View1
。
各 View
的层级关系如下:
View1 -> View11 -> View111
View1 -> View12
具体布局代码如下:
// view 1
let view1 = View1(frame: CGRect(x: 40, y: 100, width: 300, height: 400))
view1.backgroundColor = UIColor.yellow
self.view.addSubview(view1)
// view 1_1
let view11 = View11(frame: CGRect(x: 20, y: 30, width: 100, height: 120))
view11.backgroundColor = UIColor.green
view1.addSubview(view11)
// view 1_1_1
let view111 = View111(frame: CGRect(x: 10, y: 10, width: 60, height: 40))
view111.backgroundColor = UIColor.gray
view11.addSubview(view111)
// view 1_2
let view12 = View12(frame: CGRect(x: 20, y: 200, width: 100, height: 120))
view12.backgroundColor = UIColor.red
view1.addSubview(view12)
当一个触摸事件发生在灰色(View 1_1_1
)区域时,hitTest
被调用的顺序如下。
View 1 hitTest
View 1_2 hitTest
View 1_1 hitTest
View 1_1_1 hitTest
表明触摸事件发生时,先从底层 View
开始逐级向上层进行遍历,直到找到灰色视图 View111
。
响应者链
寻找到合适的 View
后,将会响应对应 View
的触摸事件。
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("View 1_1_1 touchesBegan")
}
此处合适的 View
就是 View111
,将输出日志:
View 1_1_1 touchesBegan
响应者链的传递顺序与 UI 事件传递顺序正好相反,如果当前 View
(即:View 1_1_1
)无法响应触摸事件(比如:isUserInteractionEnabled = false
),将向父一级传递事件。此时,View 1_1
将收到触摸事件,如果 View 1_1
可交互的话,将响应对应的 touchesBegan
事件。
View 1_1 touchesBegan
否则继续往父一级遍历,直到找到合适的响应对象。
示例代码
GitHub: iOS-Examples/Example-View响应链