为你的TabBar添加Badge

作者: summer朱光文 | 来源:发表于2015-09-22 03:42 被阅读8551次

    本文来源

    今天写代码的时候想为我的应用添加一个tab上的红点提示功能,随便在github上搜了一下,看的都觉得麻烦,可能是我个人比较喜欢简单的东西 :-)。然后我就想自己实现一个很简单的版本,同时我又不想改变之前的UITabBarController, 所有就写了一个UITabBar的扩展类来实现badge,功能十分简单,这里和大家分享一下,如果有什么问题请多指正。

    实现过程

    首先定义了三种Badge类型

    typedef NS_ENUM(NSUInteger, CustomBadgeType){
        kCustomBadgeStyleRedDot, //显示普通红点类型
        kCustomBadgeStyleNumber, //显示数字类型
        kCustomBadgeStyleNone //不显示
    };
    

    然后提供了一个最主要的api, 设置badge

    - (void)setBadgeStyle:(CustomBadgeType)type value:(NSInteger)badgeValue atIndex:(NSInteger)index;
    

    下边是设置badge的实现, 主要思路是首先初始化所有的badgeView,比如tabbar items的数量为3,则初始化3个红点badge和数字badge,并把它们都隐藏。然后根据badge的style ,value,index设置显示哪一个红点以及显示什么样式。

    -(void)setBadgeStyle:(CustomBadgeType)type value:(NSInteger)badgeValue atIndex:(NSInteger)index{
        //判断是否已经初始化过badge,没有的话则计算并添加badge
        if( ![[self valueForKey:kBadgeViewInitedKey] boolValue] ){
            [self setValue:@(YES) forKey:kBadgeViewInitedKey];
            [self addBadgeViews];
        }
    
        //获取badge dotViews数组  和 numberViews数组, 分别代表红点badge和数字badge
        NSMutableArray *badgeDotViews = [self valueForKey:kBadgeDotViewsKey];
        NSMutableArray *badgeNumberViews = [self valueForKey:kBadgeNumberViewsKey];
        
        //设置隐藏badge
        [badgeDotViews[index] setHidden:YES];
        [badgeNumberViews[index] setHidden:YES];
        
        //根据类型决定显示哪一种badge
        if(type == kCustomBadgeStyleRedDot){
            [badgeDotViews[index] setHidden:NO];
            
        }else if(type == kCustomBadgeStyleNumber){
            [badgeNumberViews[index] setHidden:NO];
            UILabel *label = badgeNumberViews[index];
            //根据数字来动态更新badge
            [self adjustBadgeNumberViewWithLabel:label number:badgeValue];
            
        }else if(type == kCustomBadgeStyleNone){
            //empty
        }
    }
    

    这里初始化badgeViews的思路是使用tabbar的宽度计算出每一个badge的位置,然后添加UIView 和UILabel到tabbar中,并设置一些属性:

    -(void)addBadgeViews{
        //获取badgeview的横向偏移量和top值
        id idIconWith = [self valueForKey:kTabIconWidth];
        CGFloat tabIconWidth = idIconWith ? [idIconWith floatValue] : 32;
        id idBadgeTop = [self valueForKey:kBadgeTop];
        CGFloat badgeTop = idBadgeTop ? [idBadgeTop floatValue] : 11;
        
        NSInteger itemsCount = self.items.count;
        CGFloat itemWidth = self.bounds.size.width / itemsCount;
        
        //dotBadge views
        NSMutableArray *badgeDotViews = [NSMutableArray new];
        for(int i = 0;i < itemsCount;i ++){
            UIView *redDot = [UIView new];
            redDot.bounds = CGRectMake(0, 0, 10, 10);
            redDot.center = CGPointMake(itemWidth*(i+0.5)+tabIconWidth/2, badgeTop);
            redDot.layer.cornerRadius = redDot.bounds.size.width/2;
            redDot.clipsToBounds = YES;
            redDot.backgroundColor = [UIColor redColor];
            redDot.hidden = YES;
            [self addSubview:redDot];
            [badgeDotViews addObject:redDot];
        }
        //设置属性来记录有哪些dotViews,方便更新dotViews的属性时使用
        [self setValue:badgeDotViews forKey:kBadgeDotViewsKey];
        
        //numberBadge views
        NSMutableArray *badgeNumberViews = [NSMutableArray new];
        for(int i = 0;i < itemsCount;i ++){
            UILabel *redNum = [UILabel new];
            redNum.layer.anchorPoint = CGPointMake(0, 0.5);
            redNum.bounds = CGRectMake(0, 0, 20, 14);
            redNum.center = CGPointMake(itemWidth*(i+0.5)+tabIconWidth/2-10, badgeTop);
            redNum.layer.cornerRadius = redNum.bounds.size.height/2;
            redNum.clipsToBounds = YES;
            redNum.backgroundColor = [UIColor redColor];
            redNum.hidden = YES;
    
            redNum.textAlignment = NSTextAlignmentCenter;
            redNum.font = [UIFont systemFontOfSize:12];
            redNum.textColor = [UIColor whiteColor];
            
            [self addSubview:redNum];
            [badgeNumberViews addObject:redNum];
        }
        //设置属性来记录有哪些numberViews,方便更新numberViews的属性时使用
        [self setValue:badgeNumberViews forKey:kBadgeNumberViewsKey];
    }
    

    上边还有一个函数是根据数字更新badge,实现也很简单武断,如下:

    -(void)adjustBadgeNumberViewWithLabel:(UILabel *)label number:(NSInteger)number{
        [label setText:(number > 99 ? @"..." : @(number).stringValue)];
        if(number < 10){
            label.bounds = CGRectMake(0, 0, 14, 14);
        }else if(number < 99){
            label.bounds = CGRectMake(0, 0, 20, 14);
        }else{
            label.bounds = CGRectMake(0, 0, 20, 14);
        }
    }
    

    看完上边,其实大概的逻辑流程都讲清楚了。如果你细心的话,会发现[self setValue:badgeNumberViews forKey:kBadgeNumberViewsKey];这一句调用是有问题的,如果不处理程序会直接崩溃。我本来是想在extension中定义几个的属性,写起来才觉得很麻烦,因此在stackoverflow上稍微看了几个回答,采用了一个简单点的方法: 重写valueForUndefinedKey:setValue:forUndefinedKey:,并利用objc的动态绑定的方式注入属性,这里其实会有一些陷阱,可以参考我最后附的链接进行详细阅读。代码如下:

    -(id)valueForUndefinedKey:(NSString *)key{
        return objc_getAssociatedObject(self, (__bridge const void *)(key));
    }
    -(void)setValue:(id)value forUndefinedKey:(NSString *)key{
        objc_setAssociatedObject(self, (__bridge const void *)(key), value, OBJC_ASSOCIATION_COPY);
    }
    

    如果你知道更好地方法一定要告诉我 :- )

    还有其他的一些api实现比较简单就不列出来了。

    使用方法

    使用方法相当简单,首先将github上的.h和.m文件加入你的工程,接下来只需要两步:

    1. 在初始化tabbar的时候,设置badgeview的位置
    [tabController.tabBar setTabIconWidth:29];//调整x偏移量
    [tabController.tabBar setBadgeTop:9];//调整y偏移量
    
    1. 设置显示badgeview的样式
    [self.tabBarController.tabBar setBadgeStyle:type value:number atIndex:index];
    

    大功告成!

    效果

    这里附上一个效果


    demo.gif

    思考

    这个实现还有很多满足了我最简单的需求,还有一些问题需要处理:

    • 当tabbar更新时,badgeView并没有随着更新,这里需要添加一种自动的机制来更行
    • 还有一些其他的定制比如修改badgeView的颜色,修改badgeView的大小以及badgeView显示字符串而不仅仅是数字目前并没有支持 ,可能我比较懒吧,以后慢慢加上 :-)

    参考

    本文的源代码以及demo都在github上,需要的请戳这里 MRsummer/CustomBadge。欢迎吐槽~

    相关文章

      网友评论

      • 6ee1c0c55b82:你好,我想问一下,杀了进程再进来会自动消失吗?我直接设置的badgevalue会消失,啥进程的时候
      • 十一岁的加重:.badgeValue = @"12" // 显示红底白字 `12`
        .badgeValue = @"" // 显示红底无字,并且红点很小
        .badgeValue = nil // 隐藏红点
        Jirui_:.badgeValue = @"" // 显示红底无字,并且红点很小

        红点并不小,对比微信-发现处的红点
      • 张周择:self.navigationController.tabBarItem.badgeValue = @"1"
        十一岁的加重:@pd 还是你的方法简单,就一个属性而已
      • 一个人的阳光:楼主,你可以看看WZLBadge,别人实现方式很友好,既可以自定义又不影响原生API,你的方法感觉不怎么样
        Jiang_Wang:@summer朱光文 楼主好涵养
        summer朱光文:@一个人的阳光 :smile: 好的谢谢啊 我就是写demo项目的时候懒得上github上找 就自己花一两个小时写了一个练练手 我去看看你说的那个

      本文标题:为你的TabBar添加Badge

      本文链接:https://www.haomeiwen.com/subject/drjicttx.html