实现过程

首先定义了三种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。欢迎吐槽~

文/summer朱光文(简书作者)
原文链接:http://www.jianshu.com/p/e8398e3231d5
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

为你的TabBar添加Badge-感谢分享的更多相关文章

  1. Android中通过ActionBar为标题栏添加搜索以及分享视窗

    在Android3.0之后,Google对UI导航设计上进行了一系列的改革,其中有一个非常好用的新功能就是引入的ActionBar,他用于取代3.0之前的标题栏,并提供更为丰富的导航效果.Action ...

  2. Ext TabPanel tabbar添加按钮

    tabPanel tabbar添加按钮 this.tabPanel = Ext.create('Ext.tab.Panel', { tabBar:{ items:[{ //组件靠右 xtype: 't ...

  3. mysql启动关闭的批处理,感觉很好用在其他论坛帖子上找到的,感谢分享

    最近用mysql的时间比较多,每次都在计算机管理工具下面去启动,感觉很麻烦,于是搜索了下果然有前辈已经做出了这些东西,今天收藏整理,mysql启动关闭的批处理感觉很好用在其他论坛帖子上找到的,感谢互联 ...

  4. Cocos2d-x 添加iOS7默认分享/AirDrop

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢! 原文地址: http://www.cocos2dev.com/?p=530 下午添加分享的时候,看着这 ...

  5. 应用程序添加角标和tabBar添加角标,以及后台运行时显示

    1.设置角标的代码:   // 从后台取出来的数据可能是int型的不能直接给badgeValue(string类型的),需要通过description转化  NSString *count = [re ...

  6. iOS TabBar添加阴影

    效果图如下所示: 直接上代码 //移除顶部线条 self.tabBar.backgroundImage = [UIImage new]; self.tabBar.shadowImage = [UIIm ...

  7. flutter 自定义tabbar 给tabbar添加背景功能

    flutter 自带的tabbar BottomNavigationBar有长按水波纹效果,不可以添加背景图片功能,如果有这方面的需求,就需要自定义tabbar了 自定义图片 我们使用BottomAp ...

  8. iOS 实现脉冲雷达以及动态增减元素 By Swift-感谢分享

    Swift经过Xcode6 Beta4一版更新后,基本上已经可以作为生产工具了,虽然有一些地方和ObjC比起来要“落后”一些,但也无伤大雅.这里就用Xcode6 Beta4+iOS SDK 8.0开发 ...

  9. Swift 标签控制器(tabbar添加提醒和控制器)

    // Override point for customization after application launch. //初始化window, 大小为设备物理大小 self.window = U ...

随机推荐

  1. Cocos2D-x培训课程

    1.1 Cocos2D-x 什么是cocos2d-x cocos2d-x在游戏开发中的运用 cocos2d-x的几个重要版本特点 iOS环境下搭建cocos2d开发环境 windows平台搭建coco ...

  2. su: cannot set user id: Resource temporarily unavailable

    今天R&D所在主机出现su: cannot set user id: Resource temporarily unavailable资源不可用报错,直接通过其他机器ssh huyuh@xxx ...

  3. SharePoint 文档库实现文件夹拖放到文档库

    打开文档库-> 选择文件夹-> 在Ribbon中选择“库(list)”-> 在右边可以看到打开方式-> 选择用资源管理器打开-> 在新打开的资源管理器中可能实现对文夹的拖 ...

  4. magento 数据结构

    首先要知道是EAV模式,这种结构要比普通数据结构更容易扩展,但是带来的就是查询速度慢,好在magento的缓存机制不错 最重要的3张表eav_entity_type,eav_entity_attrib ...

  5. 高级Magento模型 EAV

    我们讲过Magento有两种模型,简单模型和EAV(Entity Attribute Value)模型.上一章我们讲过所有的Magento模型都是继承自Mage_Core_Model_Abstract ...

  6. 并行计算vs分布式计算

    一般认为,集中在同一个机柜内或同一个地点的紧密耦合多处理机系统或大规模并行处理系统是并行处理系统,而用局域网或广域网连接的计算机系统是分布式处理系统.松散耦合并行计算机中的并行操作系统有时也称为分布式 ...

  7. OLEDB读取EXCEL表格时,某些字段为空,怎么办?

    转载:http://blog.sina.com.cn/s/blog_53864cba01011cbn.html   前些日子,写了一个Excel导入数据库的共同Batch,突然有一天发现当我修改Exc ...

  8. C#类和成员定义

    1 定义类     C#用关键字class来定义类.默认情况下,类声明为内部(internal)的,即只有当前项目中的代码才能访问它.与之相对应的,还可以用public关键字来修饰,这样该类还可以由其 ...

  9. JS修改JSON中key的方法

    function modifyJosnKey(json,oddkey,newkey){ var val=json[oddkey]; delete json[oddkey]; json[newkey]= ...

  10. 仿主题广告轮播js

    function SlideShow(c) { var a = document.getElementById("slide"); var f = document.getElem ...