02、iOS面试-事件传递&视图响应链
2020-03-19 本文已影响0人
程序萌
先说下事件传递:
#事件传递的两个核心方法
#第一个方法返回一个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
-
事件传递的流程
image.png - 事件传递图示 (选自链接:https://www.jianshu.com/p/2e074db792ba)
image.png
如果想让某个view不能处理事件(或者说,事件传递到某个view那里就断了),那么可以通过刚才提到的三种方式。比如,设置其userInteractionEnabled = NO;那么传递下来的事件就会由该view的父控件处理。
例如,不想让蓝色的view接收事件,那么可以设置蓝色的view的userInteractionEnabled = NO;那么点击黄色的view或者蓝色的view所产生的事件,最终会由橙色的view处理,橙色的view就会成为最合适的view。
所以,不管视图能不能处理事件,只要点击了视图就都会产生事件,关键在于该事件最终是由谁来处理!也就是说,如果蓝色视图不能处理事件,点击蓝色视图产生的触摸事件不会由被点击的视图(蓝色视图)处理!
注意:如果设置父控件的透明度或者hidden,会直接影响到子控件的透明度和hidden。如果父控件的透明度为0或者hidden = YES,那么子控件也是不可见的!
- 流程描述
- 我们点击屏幕产生触摸事件,系统会将这个事件加入到一个由UIApplication管理的事件队列中,UIApplication会从消息队列里取事件分发下去,首先传给UIWindow
- 在UIWindow中就会调用 hitTest:withEvent: 方法去返回一个最终响应的视图
- 在hitTest:withEvent方法中就会去调用第二个方法 pointInside:withEvent: 去判断当前点击的point是否在UIWindow范围内,如果是的话,就会去遍历它的子视图来查找最终响应的子视图
同级view的遍历方式是使用倒序的方式来遍历子视图
,也就是说最后添加的子视图会最先遍历
,在每一个视图中都去回去调用它的 hitTest:withEvent: 方法,可以理解成为是一个递归调用- 最终会返回一个响应视图,如果返回的视图有值,那么这个视图就作为最终响应视图,结束整个事件传递,如果没有值,那么就会将UIWindow作为响应值
- hitTest:withEvent: 方法的流程
- 首先会判断当前视图的hiden属性、是否可以交互及透明度是否大于0.01,如果满足条件则进入下一步,否则返回nil
- 然后调用pointInside:withEvent:方法来判断
这个点
是否在当前范围内,如果满足条件进入下一步,否则返回nil- 返回以
倒序
的方式遍历它的子视图,在每一个子视图中取调用hitTest:withEvent: ,如果有一个子视图返回了一个最终的响应视图,就将这个视图返回给调用方,结束流程。如果全部遍历完成都没有找到一个最终响应视图,因为点击位置在当前视图范围内,就将当前视图作为最终响应视图返回
再来说下视图响应链:
- 点击、摇动、滑动、旋转等会被系统封装成UIEvent,放到事件队列里等待UIApplication去取,然后寻找响应者,找到对应的方法并执行的过程就是响应。
通过上述的传递事件会找到第一响应者,这时就用第一响应者来响应。
如果第一响应者不响应,不响应的传递流程示图
点击当前视图
initial View
- initial View的父视图view
- view controller
- UIWindow
- UIApplication
- 通过代码来展示
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"%@ touch begin", self.class);
UIResponder *next = [self nextResponder];
while (next) {
NSLog(@"下一个响应者%@",next.class);
next = [next nextResponder];
}
}
-
打印结果
image.png - 如果传递到UIApplication也没有响应,则这个事件作废