响应者对象(UIResponder)
继承了UIResponder的对象才能接受并处理事件,我们称之为“响应者对象”。以下都是继承UIResponder的,所以都能接收并处理事件。
1 | UIApplication |
为什么继承了UIResponder的类就能够接收并处理事件呢?
因为UIResponder中提供了以下4个对象方法来处理触摸事件
1 | //UIResponder内部提供了以下方法来处理事件触摸事件 |
事件的产生和传递
事件的产生
- 发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中
- UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常先发送事件给应用程序的主窗口(keywindow)。
- keywindow会在视图层次结构中找到一个最合适的视图来处理触摸事件。
寻找处理事件最合适的view
- 首先判断主窗口(keyWindow)自己是否能接受触摸事件
- 判断触摸点是否在自己身上
- 子控件数组中从后往前遍历子控件,重复前面的两个步骤(所谓从后往前遍历子控件,就是首先查找子控件数组中最后一个元素,然后执行1、2步骤)
- view,比如叫做hitView,那么会把这个事件交给这个hitView,再遍历这个hitView的子控件,直至没有更合适的view为止。
- 如果没有符合条件的子控件,那么就认为自己最合适处理这个事件,也就是自己是最合适的view。
UIView不能接收触摸事件的三种情况:
- 不允许交互:userInteractionEnabled = NO
- 隐藏:如果把父控件隐藏,那么子控件也会隐藏,隐藏的控件不能接受事件
- 透明度:如果设置一个控件的透明度<0.01,会直接影响子控件的透明度。alpha:0.0~0.01为透明
Code
1 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event |
拦截事件的处理
通过重写hitTest:withEvent:就可以拦截事件的传递过程,想让谁处理事件谁就处理事件。
注意
如果hitTest:withEvent:方法中返回nil,那么调用该方法的控件本身和其子控件都不是最合适的view,也就是在自己身上没有找到更合适的view。那么最合适的view就是该控件的父控件。
技巧
想让谁成为最合适的view就重写谁自己的父控件的hitTest:withEvent:方法返回指定的子控件,或者重写自己的hitTest:withEvent:方法 return self。但是,建议在父控件的hitTest:withEvent:中返回子控件作为最合适的view!
原因是子控件在遍历时会根据addsuview的逆序遍历。