长路漫漫,唯剑作伴--响应者链
一、事件响应链
-
由离用户最近的View向系统传递,即UIAplication----最近的view
二、事件传递链
- 由系统向离用户最近的View传递,即最近的view----UIAplication
三、事件分发
-
第一响应者
-
指的是当前接受触摸的响应者对象(通常是一个UIView对象),即表示当前该对象正在与用户交互,它是响应者链的开端。整个响应者链和事件分发的使命都是找出第一响应者。
-
成为第一响应者:becomeFirstResponderre
-
注销第一响应者:signFirstResponder
-
-
命中测试
-
iOS系统检测到手指触摸(Touch)操作时会将其打包成一个UIEvent对象,并放入当前活动Application的事件队列,单例的UIApplication会从事件队列中取出触摸事件并传递给单例的UIWindow来处理,UIWindow对象首先会使用hitTest:withEvent:方法寻找此次Touch操作初始点所在的视图(View),即需要将触摸事件传递给其处理的视图,这个过程称之为hit-test view。
-
-
过程:假如用户点击了E,下面结合下图说明hit-Tist View de 流程
-
A是UIWindow的根视图,因此,UIWindwo对象会首相对A进行hit-test;
- 显然用户点击的范围是在A的范围内,因此,pointInside:withEvent:返回了YES,这时会继续检查A的子视图;
-
这时候会有两个分支,B和C:
-
点击的范围不再B内,因此B分支的pointInside:withEvent:返回NO,对应的hitTest:withEvent:返回nil;
-
点击的范围在C内,即C的pointInside:withEvent:返回YES;
-
-
这时候有D和E两个分支:
-
点击的范围不再D内,因此D的pointInside:withEvent:返回NO,对应的hitTest:withEvent:返回nil;
-
点击的范围在E内,即E的pointInside:withEvent:返回YES,由于E没有子视图(也可以理解成对E的子视图进行hit-test时返回了nil),因此,E的hitTest:withEvent:会将E返回,再往回回溯,就是C的hitTest:withEvent:返回E--->>A的hitTest:withEvent:返回E。
-
-
至此,本次点击事件的第一响应者就通过响应者链的事件分发逻辑成功的找到了。
-
四、超出父视图的部分如何响应
- 当存在view时才会传递对应的event,现在点击了父视图以外的范围,自然返回的是nil。所以当子视图(比如按钮btn)因为一些原因超出了父视图范围,就要重写hittest方法,让其返回对应的子视图,来接收事件。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView *view = [super hitTest:point withEvent:event]; if (view == nil) { CGPoint tempoint = [btn convertPoint:point fromView:self]; if (CGRectContainsPoint(btn.bounds, tempoint)) { view = btn; } } return view; }