为你的TabBar添加Badge

本文来源

今天写代码的时候想为我的应用添加一个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。欢迎吐槽~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,158评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,600评论 1 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,785评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,655评论 0 220
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,075评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,002评论 1 225
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,146评论 2 318
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,918评论 0 211
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,671评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,838评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,318评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,636评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,343评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,187评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,982评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,126评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,934评论 2 279

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,111评论 18 139
  • 今天连着看到几篇对已逝之人遗憾的文章,这几个逝去的人,当然可能是碰巧,都人品高尚,隐忍坚强,在哲学方面深有造诣。 ...
    a62013c69d93阅读 198评论 0 0
  • 测试一小下
    manylu阅读 97评论 0 0
  • 作者:樱花幽梦 好久不见 是岁月回眸后的第一次印象 重逢在又一个路口的转角 数不尽的流年匆匆 此去经年 境遇在不经...
    樱花幽梦阅读 208评论 0 0