一.响应者链
响应者链UIResponser 包括了 触摸信息的处理,比如开始、移动、停止等等。常见的UIresponser有UIView及其子类,UIViewController,AppDelegate,UIApplication等等。
响应者链是由UIResponser组成的。,他是是按照哪种规则组成的呢?
A: 程序启动UIApplication会生成一个单例,并会关联一个APPDelegate。AppDelegate作为整个响应链的根建立起来,而UIApplication会将自己与这个单例链接,即UIApplication的下一个responser为AppDelegate。
B:创建UIWindow程序启动后,任何的UIWindow被创建时,UIWindow内部都会吧nextResponser 设置为UIApplication单例。UIWindow初始化rootViewController,rootViewController的nextResponser会设置为UIWindow。
C:UIViewController初始化loadView VC的view的nextResponser会设置为VC。
D:addSubView的过程中,如果subView不是VC的View,那么subView的nextResponser会被设置为superView。如果是VC的View,那就是 subView -> subView.VC ->superView如果在中途,subView.VC被释放,就会变成subView.nextResponser = superView
二.事件传递链
事件传递通过两种方法来做这个事情。
// 先判断点是否在View内部,然后遍历subViews
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
//判断点是否在这个View内部
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;
流程
1:先判断该层级是否能够响应(1.alpha>0.01 2.userInteractionEnabled == YES 3.hidden = NO)
2:判断该点是否在view内部 ,
3:如果在那么遍历子view继续返回可响应的view,直到没有。
hitTest:withEvent:方法的处理流程如下:
首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内;
若返回NO,则hitTest:withEvent:返回nil;
若返回YES,则向当前视图的所有子视图(subviews)发送hitTest:withEvent:消息,所有子视图的遍历顺序是从top到bottom,即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕;
若第一次有子视图返回非空对象,则hitTest:withEvent:方法返回此对象,处理结束;
如所有子视图都返回非,则hitTest:withEvent:方法返回自身(self)。
常见问题
1.父view设置为不可点击,子view可以点击吗
回答:不可以,hit test 到父view就截止了
子view设置view不可点击不影响父类点击
实际用法
点一一个圆形控件,如何实现只点击圆形区域有效,重载pointInside。此时可将外部的点也判断为内部的点,反之也可以。
事件响应链在复杂功能界面进行不同控件间的通信,简便某些场景下优于代理和block