结合一道面试题浅谈触摸事件的流程
2018-10-25 本文已影响6人
dj_rose
题.png
条件:红色View 是蓝色View的子视图、蓝色View是绿色View的子视图
问:点击超出蓝色View的红色View 会有反应吗?如果没有,有什么简单的办法让其响应红色view的事件?
一看到这种问题,大多数人的第一想法就是重写蓝色view的hitTest方法,同时重写蓝色view的pointInside:方法。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if ([self pointInside:point withEvent:event]) {
return 红色view;
}
return [super hitTest:point withEvent:event];
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if (坐标在红色view内) {
return YES;
}
//否则返回默认的操作
return [super pointInside:point withEvent:event];
}
通过这两个方法配合,将最佳响应者改变为红色view。
群里有大佬就是这么回答的。然而面试官似乎并不满意,又问:有没有更简单的?
有的同学说:改变下view的层级吧,这样最简单~
很显然,面试官又补充了一点:不能改变现有视图层级、不能改变view的大小。
这下就有点懵逼了。。。
记得之前看过一篇触摸事件的文章,讲的很详细--iOS触摸事件全家桶
于是又去翻看了下,边看边思考,面试官问的这个更简单的方法到底是想考察哪一块的知识点?
回顾完那篇文章,简单总结下触摸事件的流程:
系统响应 --> 传递给前台app --> 寻找事件最佳响应者(hit-testing)--> 事件的响应及在响应链中的传递
不难发现,我们上面的hitTest:解决方案是在寻找事件最佳响应者(hit-testing)的过程中去改变了最佳响应者。那么,面试官所说的更简单的方法,是不是在事件最佳响应者确定了之后,事件响应过程中去拦截处理?
答案:是的!
可能是面试官想要的更简单的方案
在寻找事件的最佳响应者的过程中,事件自下而上进行传递。
UIApplication ——> UIWindow ——> 子视图 ——> ... ——> 子视图
最佳响应者确定之后,事件从最佳响应者开始,自上而下的进行事件的响应及传递。
响应者对于事件的拦截以及传递都是通过 touchesBegan:withEvent: 方法控制的,该方法的默认实现是将事件沿着默认的响应链往下传递。
因此我们可以简单的通过重写控制器VC的touchesBegan:方法来拦截红色view的点击事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:_blueView];
if (CGRectContainsPoint(_redBtn.frame, point)) {
[self clickTest];
}
}
写到这里,感叹出题者对于触摸事件的考察点理解的真的是相当透彻,发人深省~