深入浅出iOS事件机制

在不断填坑中前进。。

Posted by 三十一 on April 5, 2017

深入浅出iOS事件机制

什么是响应链

响应链是一堆可以处理事件的对象构成的具有先后循序的链条。 iOS 中有一个类叫做 UIResponder ,它的头文件如下图:

UIResponder 是可以响应事件的的类的基类,只有继承自 UIResponder的类才可以响应用户的操作。包括最常见的 UIViewUIWindowUIViewController等。

在我们的app中,所有的视图都是按照一定的结构组织起来的,即树状层次结构,每一个View 都有自己的superView。当一个View被添加到superView上时,View的nextResponder就被指向superView,UIViewController 的self.view 被添加到superView上时,self.view 的 nextResponder 指向UIViewControllerUIViewControllernextResponder 指向superView。这样整个app 就通过 nextResponder 形成了一条链,也就是我们说的响应链。

如何确认第一个响应者

响应链建立后,当用户点击屏幕时,如何确认第一个响应者?

每当手指接触屏幕,UIApplication接收到手指的事件之后,就会去调用UIWindow的- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; ,看看当前点击的点是不是在window内,如果点击的点不在UIWindow内,第一响应者就是UIApplication,如果点击的点在UIApplication内部,则继续依次调用window的subView的- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; 方法,查看点击的点是否在当前subView试图内,如果不在则第一响应者就是window,如果在就继续上面的流程,遍历子视图,直到找到最终的视图。最后找到的视图就是第一个响应者。

其实只是单纯的说点击的点在不在视图内是不准确的,因为- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; 方法判断的不只是点范围的问题,具体的还包括视图是否隐藏、视图是否可交互、视图的透明度是否大于0.01等,这些都满足后,才能确认这个视图是否响应事件。下面这张图很清楚的画出了这个流程:

其实还有一种容易疑惑的情景,当两个视图有重叠时,如何确认那个视图响应事件。

当用户点击红色和蓝色重叠的部分时,系统是如何确认红色和蓝色那个视图响应事件呢?

这就牵扯到subview调用- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event的顺序,调用的顺序是根据subview的index顺序,index越大越先被访问(越后被添加到父视图,index越大)。如果确认是蓝色视图响应事件,红色视图的- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event方法就不会再被调用。

响应链的工作机制

当确认了第一个响应者后,事件就被传递到这个响应者处理。 如果他不出理,就会通过nextResponder传递到下一个响应者处理,知道找到一个响应者处理,或者到了AppDelegate仍未找到响应者处理,事件丢弃。

可以用来做什么

最常见的应用就是改变按钮的点击相应区域。


参考链接

事件传递响应链

深入浅出iOS事件机制 iOS Events and Responder Chain