一、需求背景
现在很多应用都有类似下图这种UI
父视图是图中蓝色框大小,中间按钮凸起部分在视图外,在这种情况下如果我们不做任何处理,点击图中红色区域是无法被响应的。
二、介绍
响应链在这里就不过多介绍了,我们主要介绍事件分发时是如何找到最合适的事件处理者。
这里需要了解两个方法:
// 判断点击区域是否在当前视图范围内
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
// 最适合处理事件的View
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
方法一用于判断点击区域是否在当前视图范围内
方法二用于寻找最适合的View
我们以上图的例子进行说明,暂时称呼它为tabbar,其视图范围为图中1区域,中间凸起部分button在视图外的部分为2区域,两者重合部分为3区域。
在点击1、2、3区域时都会先调用tabbar的- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
,只不过返回值不同,结果不同,我们详细来看看会发生什么。
1.1、点击区域1
tabbar的方法一返回YES,接着会去遍历tabbar上所有子控件并调用子控件中的方法一,也就是会调用button的方法一,由于button不在1区域内,所以button的方法一返回NO,并且button的方法二返回null,然后调用tabbar的方法二并返回self即tabbar本身处理点击事件。
1.2、点击区域2
tabbar的方法一返回YES,接着会去遍历tabbar上所有子控件并调用子控件中的方法一,也就是会调用button的方法一,所以button的方法一返回YES,并且button的方法二返回本身,然后调用tabbar的方法二并返回button,最终由button处理点击事件。
1.3、点击区域3
我们在自定义tabbar时,一般有两种方式:
1、将自定义的tabbar添加到原来的tabbar上。
2、利用KVC替换掉原来的tabbar。
方式一
事件分发是由下往上进行分发,在调用系统tabbar方法一的时候,返回值为NO,事件不会继续向上分发,即不会调用自定义的tabbar的那两个方法,由系统的tabbar成为事件的接收者。此时无法实现我们的需求。
方式二
我们知道最终的接收者实际上是通过tabbar的方法二的返回值决定的,所以我们只需要修改自定义tabbar的方法二的实现即可。
// 其中self.plusButton为中间凸起按钮
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
CGPoint newPoint = [self convertPoint:point toView:self.plusButton];
// 判断是否属于按钮范围
if ([self.plusButton pointInside:newPoint withEvent:event]) {
return self.plusButton;
} else {
return [super hitTest:point withEvent:event];
}
}
三、总结
在做类似这种UI时,需要将自定义tabbar通过KVC替换掉原有tabbar,并重写- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
返回合适的View去处理事件。