市场有相当一部分的app底部的tabBar是张这样的
中间蓝色的圆形区域可以响应点击事件,一般情况下,水平线上方的部分点击是没有反应的。今天就来讨论下这块区域响应的处理方式。
首先介绍下中间蓝色区域的实现方式。系统默认的tabBarItem是没有办法做到这点的,这里是在self.tabBar上添加一个view,用这个view来完整覆盖self.tabBar,所以这个view才是用来真正显示tabBarItem的地方。本文用的是LLTabBar,这里不做展开,感兴趣的伙伴去查下。
app启动后,我们打开xcode的hierarchy。可以看到如下的层次结构。
我们知道iOS的触摸发生后,是沿着响应链找到最终响应触摸事件的view。在遇到分支时,首先会先判断最上方的分支是否能响应该事件(view本身或者是它的子view),如果能够响应的话,就不会走另外一条分支了。那么分支如何判断是否能够响应呢?答案就在以下这两个方法
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
我们可以做个试验,针对UITabBar,因为UITabBar位于view层次的上方,触摸事件会先被它过滤。
我们新建一个UITabBar的类别,在类别里面写上方法
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
return YES;
}
之后启动app,我们尝试点击其他不属于UITabBar的区域,发现所有的点击都失效了。因为触摸事件发生后,沿着响应链来到UILayoutContainerView时碰到了分支,由于UITabBar位于上方,所以会先调用UITabBar的pointInside: withEvent:和hitTest: withEvent:方法,而pointInside: withEvent:方法我们永远返回YES了,导致事件没法继续传递给它的兄弟view。
回到本篇的主题上,为了要解决该问题,单独只是重写这个方法还不够,因为我们还要指定具体是哪个view要响应。所以还必须重写hitTest: withEvent:方法。(实践证明,就算只是重写hitTest: withEvent:方法也是可以的。)
在刚刚UITabBar的类别里面添加如下的代码:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
//如果点击区域就是在UITabBar范围内的话,就直接返回
UIView *view = [super hitTest:point withEvent:event];
if(view){
return view;
}
if(view == nil){
//找到中间的按钮
UIView *publishBtn;
[self getPublishBtn:self accordView:&publishBtn];
if(publishBtn != nil){
//判断事件的触发点是否在中间按钮的区域内,不是返回nil
CGPoint newPoint = [publishBtn convertPoint:point fromView:self];
if(CGRectContainsPoint(publishBtn.bounds, newPoint)){
return publishBtn;
}
}
}
return nil;
}
-(void)getPublishBtn:(UIView*)superView accordView:(UIView**)accordView{
for(UIView *view in [superView subviews]){
if([view isKindOfClass:[LLTabBarItem class]]){
LLTabBarItem *item = (LLTabBarItem*)view;
if(item.tabBarItemType == LLTabBarItemRise){
for(UIView *subView in [item subviews]){
if([subView isKindOfClass:[UIImageView class]]){
*accordView = subView;
}
}
}
}
if([view subviews].count > 0){
[self getPublishBtn:view accordView:accordView];
}
}
}
这样,即使触发点落在UITabBar的区域之外,一样可以触发中间按钮的点击事件。
这里需要注意的是pointInside: withEvent:和hitTest: withEvent:方法不要写在子view里面,因为一旦UITabBar的这两个方法判断不再它的响应范围内,就不会再去询问子view。
如果本文让你有那么点觉得“I get”的感觉,请点个赞呗!写作很辛苦,路过请点赞!