在 UIKit 中我们使用响应者对象(Responder)接收和处理事件。一个响应者对象一般是 UIResponder 类的实例,它常见的子类包括 UIView,UIViewController 和 UIApplication,这意味着几乎所有我们日常使用的控件都是响应者,如 UIButton,UILabel 等等。
点击后,事件传递由Application->UIWindow->ViewController->View这样传递上来(事件传递是由父到子)
在这过程中,系统会去调用两个方法,如下
//事件传递的两个核心方法
//第一个方法返回一个UIView,是用来寻找哪一个视图来响应这个事件
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; // recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
//第二个方法是用来判断某一个点击的位置 是否在视图范围内,如果在就返回YES
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event; // default returns YES if point is in bounds
hitTest遍历寻找first responder 的规则:
1、调用pointInside来判断点击是否落在当前视图
2、如果pointInside返回NO,则hitTest:withEvent返回nil,查找其他同级的view
3、如果pointInside返回YES,则继续遍历subViews(最后添加的view优先被查找,FILO原则),向subView发送hitTest查找响应的view。
4、如果有子视图返回非空对象,那么hiTest:withEvent返回此对象,处理结束。(该子视图没有subview,否则应该继续遍历该子视图的subView)
5、如果全部的子视图都返回nil,则hitTest:withEvent返回自身(self)
事件的传递是从上到下(父控件到子控件),事件的响应是从下到上(顺着响应者链条向上传递:子控件到父控件。