最近在面试中看到有个关于事件传递和响应者链条的知识点,自己之前了解的不是特别清楚,因此特意查找了相关资料,梳理了下这个知识点,特此记录下,以便后续使用。
遇到问题是:如果一个按钮的范围超出了父控件的范围,问这个按钮是否能显示全,以及点击按钮是否能正常响应?如果不能怎么处理能让它响应。
这个场景在开发中也比较常见,问题描述如下图所示:
从图中可以看出,黄色按钮虽然大小超出了父控件的范围,但是还是能正常显示。点击黄色按钮和绿色按钮相交的地方,可以正常响应,但是点击黄色按钮和绿色按钮不相交的地方就没有反应了。
造成这样的原因实际上就是事件的传递和响应者链条的原因。下面来说为什么
- 用户点击屏幕触发的是事件,但是屏幕上有那么多控件,怎么确定点击的是哪个控件,以及点击的事件由哪个控件响应
- 这个问题实际就是考察苹果事件的传递和响应者链条的知识
- 事件的传递是从下往上传递的;点击屏幕的事件由系统获取到传递到应用程序的AppDelegate,再传递到window,再到root ViewController的view,再到view的子视图这样一级一级传下去的
- 响应者链条刚好和事件传递方向相反,是从上往下传递的,先看最上边的能否响应事件,如果不能响应找父控件,一直类推,找到最后如果都不能响应事件就把事件移除
找的过程大概如下代码:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) {
return nil;
}
if ([self pointInside:point withEvent:event]) {
for (UIView *subView in [self.subviews reverseObjectEnumerator]) {
CGPoint convertedPoint = [subView convertPoint:point fromView:self];
UIView *hitTestView = [subView hitTest:convertedPoint withEvent:event];
if (hitTestView) {
return hitTestView;
}
}
return self;
}
return nil;
}
如果想让黄色的按钮响应点击事件,从上边代码可以看出,需要把父控件的hitTest方法中的,判断点击的点,是否在当前试图的判断注释掉,才能执行到子视图的判断中。
因此:如果想让黄色视图还能响应事件,需要重写父视图的hitTest方法,并把判断点击的点是否在视图内的判断注释掉就可以了