闲来无事 观摩别人的项目 常常发现对UIView的hitTest:withEvent:方法的重写,以前也查过这个方法的用法作用,但是时间一长又忘记了。今天再次看到,就记录一下.
用户触摸屏幕后事件的传递过程
当用户点击屏幕后,UIApplication先响应事件,然后传递给UIWindow。如果window可以响应。就开始遍历window的subViews。遍历的过程中,如果第一个遍历的View1可以响应,那就遍历View1的SubViews。(依次这样不停地查找,直至查找到合适的响应事件view)。如果view1不可以响应,那就开始对view2进行判断和子视图的遍历。依次类推view3,view4…… 如果最后没有找到合适的响应view,这个消息就会被抛弃。(整个遍历的过程就是树的先序遍历)。过程如下图:
了解了上面的图后,我们可以看一下下面两个方法
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; // 方法A - (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event; // 方法B
对View重写这两个方法后,就会发现,点击屏幕后,首先响应的是方法A
如果方法A中我们没有调用父类的这个方法 那就根据方法A的返回View,作为响应事件的View。(如果返回nil 就是这个View不响应事件)
如果方法A中,我们调用了父类的这个方法,也就是
[super hitTest:point withEvent:event];那这个时候系统就要调用方法B;通过这个方法的返回值,来判断当前这个view能不能响应消息。
如果方法B返回的是no,那就不用再去遍历它的子视图。方法A返回的view就是可以响应事件的view。
如果方法B返回的是YES,那就去遍历它的子视图。(就是上图我们描述的那样,找到合适的view返回,如果找不到,那就由方法A返回的view去响应这个事件。)
因此总结下来:
//返回一个view来响应事件 (我们如果不想影响系统的事件传递链,在这个方法内,最好调用父类的这个方法)
- (nullableUIView *)hitTest:(CGPoint)point withEvent:(nullableUIEvent *)event;
//返回的值可以用来判断是否继续遍历子视图(返回的根据是触摸的point是否在view的frame范围内)
- (BOOL)pointInside:(CGPoint)point withEvent:(nullableUIEvent *)event;