点击了屏幕上的一个View,事件是怎么找到这个View的呢?这个View又是怎么响应这个传递过来的事件的呢?
事件传递
当点击了屏幕上的一个View后,系统会产生一个UIEvent
事件,这个事件被加入到由UIApplication
管理的一个事件队列中,UIApplication
会从事件队列中取出最前面的事件,然后再传递给UIWindow
,最后UIWindow
通过- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
和- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;
在视图层次结构中找到一个最合适的view来处理触摸事件。
如果父控件不能接收触摸事件,那么子控件就不可能接收到触摸事件
view 不能处理触摸事件的情况:
- userInteractionEnabled = NO
- hidden = YES
- alpha = 0.0~0.01(临界点待确定)
查找最合适的view来处理事件
1.自己是否能接收触摸事件?否事件传递结束
2.触摸点是否在自己身上?否事件传递结束
3.从后往前遍历子控件,重复前面的两步
4.如果没有符合条件的子控件,那么就自己最合适处理
1 | //调用时机:只要有一个事件传递到控件上,就会调用控件的这个方法。point是基于方法调用者的坐标系 |
模拟系统,实现hitTest方法
1 | - (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event |
当一个View的超出了它的父视图,点击时,是无法接受事件的,如果想让它接收事件,可以通过下面方式实现
代码实现如下
1 | //BottomView.m中 |
事件响应
响应者链的事件传递过程:
1>如果当前view是控制器的view,那么控制器就是上一个响应者,事件就传递给控制器;如果当前view不是控制器的view,那么父视图就是当前view的上一个响应者,事件就传递给它的父视图
2>在视图层次结构的最顶级视图,如果也不能处理收到的事件或消息,则其将事件或消息传递给window对象进行处理
3>如果window对象也不处理,则其将事件或消息传递给UIApplication
对象
4>如果UIApplicatio
n也不能处理该事件或消息,则将其丢弃
总结
事件处理的整个流程总结:
1.触摸屏幕产生触摸事件后,触摸事件会被添加到由UIApplication管理的事件队列中(即,首先接收到事件的是UIApplication)。
2.UIApplication会从事件队列中取出最前面的事件,把事件传递给应用程序的主窗口(keyWindow)。
3.主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件。(至此,第一步已完成)
4.最合适的view会调用自己的touches方法处理事件
5.touches默认做法是把事件顺着响应者链条向上抛。
事件的传递和响应的区别:
- 事件的传递是从上到下(父控件到子控件)
- 事件的响应是从下到上(顺着响应者链条向上传递:子控件到父控件)
QA
Q:增大Button的点击区域
A:
重写Button的pointInside
方法
1 | - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event |
Q: 通过View找到它所在的ViewController
A:
通过nextResponder
方法找到响应链中的VC
1 | @implementation UIView (DM) |