I want to change background color of UITabBarItem
badge but can't find any resource on how to make it.

- 7,829
- 55
- 176
- 334
17 Answers
UITabBarItem
has this available since iOS 10.
var badgeColor: UIColor? { get set }
It's also available via appearence.
if #available(iOS 10, *) {
UITabBarItem.appearance().badgeColor = .green
}
reference docs: https://developer.apple.com/reference/uikit/uitabbaritem/1648567-badgecolor

- 6,202
- 7
- 46
- 66
-
What happens when I still want to support iOS9? Do I need to enclose this in a `respondsToSelector` resp `#available` call? – Jens Oct 19 '17 at 14:19
-
I just now realised I never answered this question @jens. This is probably no longer useful for this question per se, but might be for similar things. But If the api does not support the feature yet, you'll have to use other means in the "else". In this case use any other ways shown in these answers. – Nuno Gonçalves Jun 16 '21 at 20:39
Changing the badge-color is now natively supported in iOS 10 and later using the badgeColor
property inside your UITabBarItem
. See the apple docs for more infos on the property.
Example:
- Swift 3:
myTab.badgeColor = UIColor.blue
- Objective-C:
[myTab setBadgeColor:[UIColor blueColor]];

- 11,422
- 8
- 28
- 49
-
1if ([myTab respondsToSelector:@selector(setBadgeColor:)]) { [myTab setBadgeColor:[UIColor blueColor]]; } – rjobidon Jan 22 '17 at 05:01
-
Can you explain how this is actually implemented? I'm a little confused. – Austin Berenyi Oct 30 '17 at 23:58
I wrote this piece of code for my app, but I have only tested it in iOS 7.
for (UIView* tabBarButton in self.tabBar.subviews) {
for (UIView* badgeView in tabBarButton.subviews) {
NSString* className = NSStringFromClass([badgeView class]);
// looking for _UIBadgeView
if ([className rangeOfString:@"BadgeView"].location != NSNotFound) {
for (UIView* badgeSubview in badgeView.subviews) {
NSString* className = NSStringFromClass([badgeSubview class]);
// looking for _UIBadgeBackground
if ([className rangeOfString:@"BadgeBackground"].location != NSNotFound) {
@try {
[badgeSubview setValue:[UIImage imageNamed:@"YourCustomImage.png"] forKey:@"image"];
}
@catch (NSException *exception) {}
}
if ([badgeSubview isKindOfClass:[UILabel class]]) {
((UILabel *)badgeSubview).textColor = [UIColor greenColor];
}
}
}
}
}
You're only able to update the badge background with an image, not a color. I have also exposed the badge label if you wanted to update that in some way.
Its important to note that this code must be called after setting the tabBarItem.badgeValue
!
EDIT: 4/14/14
The above code will work in iOS 7 when called anywhere. To get it working in iOS 7.1 call it in the view controllers -viewWillLayoutSubviews
.
EDIT: 12/22/14
Here's an updated snippet which I'm currently using. I put the code in a category extension for simplicity.
- (void)badgeViews:(void (^)(UIView* badgeView, UILabel* badgeLabel, UIView* badgeBackground))block {
if (block) {
for (UIView* tabBarButton in self.subviews) {
for (UIView* badgeView in tabBarButton.subviews) {
NSString* className = NSStringFromClass([badgeView class]);
if ([className rangeOfString:@"BadgeView"].location != NSNotFound) {
UILabel* badgeLabel;
UIView* badgeBackground;
for (UIView* badgeSubview in badgeView.subviews) {
NSString* className = NSStringFromClass([badgeSubview class]);
if ([badgeSubview isKindOfClass:[UILabel class]]) {
badgeLabel = (UILabel *)badgeSubview;
} else if ([className rangeOfString:@"BadgeBackground"].location != NSNotFound) {
badgeBackground = badgeSubview;
}
}
block(badgeView, badgeLabel, badgeBackground);
}
}
}
}
}
Then when you're ready to call it, it'll look like this.
[self.tabBar badgeViews:^(UIView *badgeView, UILabel *badgeLabel, UIView *badgeBackground) {
}];
EDIT: 11/16/15
It's been brought to my attention that some people need a little more clarity on what's happening in this code. The for loops are searching for a few views which are not publicly accessible. By checking if the views class name contains a part of the expected name, it's ensuring to reach the intended view while not setting off any possible red flags by Apple. Once everything has been located, a block is executed with easy access to these views.
It's noteworthy that the possibility exists for this code to stop working in a future iOS update. For example these internal views could one day acquire different class names. However the chances of that are next to none since even internally Apple rarely refactors classes to this nature. But even if they were to, it would be something along the title of UITabBarBadgeView
, which would still reach the expected point in code. Being that iOS9 is well out the door and this code is still working as intended, you can expect this problem to never arise.

- 7,342
- 8
- 68
- 104
-
The solution which is provided works in iOS 7 but not in iOS7.1. Can anything be done for 7.1? – CrazyDeveloper Apr 12 '14 at 09:02
-
Make sure its called in your view controllers `-viewWillLayoutSubviews`. – cnotethegr8 Apr 14 '14 at 05:32
-
7@jeffamaphone the OP asked, 'is it possible'. Never did they request for an implementation that wont break. Your reasoning for giving me a down vote is inadequate. I ask you kindly to reverse the action. – cnotethegr8 May 15 '14 at 06:03
-
When you refer to "view controller's `-viewWillLayoutSubviews`": which view controller are you talking about? The UITabBarController? – Markus Rautopuro May 21 '14 at 12:25
-
1Suggestion: put the `if (block)` condition at the top of your method: without a `block` parameter, this method does nothing anyway. – Brock Boland Dec 30 '14 at 17:40
-
Please note: if you plan on using something like this in your production code, you should expect to be rejected by Apple's review process. – Brian Sachetta Nov 11 '15 at 02:56
-
What @BrianSachetta has said is completely false. My app still has this code and has not been rejected once. I don't understand why you gave me a down vote. The OP asked a question and I provided the answer. Maybe you're confused but on stackoverflow you don't down vote an answer because it's not in alignment with your coding beliefs. The voting system is intended for the use relative to the question / answer. Not for personal feelings. – cnotethegr8 Nov 11 '15 at 05:22
-
It's not for personal feelings at all. Apple does not allow the use of private APIs in app store apps which is happening here. Sure they might not catch it but you still shouldn't do it. – Brian Sachetta Nov 12 '15 at 18:10
-
@BrianSachetta give one indication that the OP intended to release this code to the App Store. He never mentioned anything about the use of only public API's. And even if he had, my code is not using private API's. This is finding a specific subview from a public view. Which is completely acceptable with Apple. You are acting based on your personal feelings and not based on the OP's question. I will ask you to recognize your fault and undo your actions. The bottom line is the OP asked a question and I answered to the fullest of their question. You are trying to go beyond that, which is wrong. – cnotethegr8 Nov 13 '15 at 05:54
-
You are using private API's as none of those subviews / classes are made available by Apple's documentation. If you feel I'm being too harsh by down voting due to something not specifically asked in the question, that's fine. But I'm not changing my vote until someone tells me I'm not allowed to down vote for that reason. – Brian Sachetta Nov 13 '15 at 15:22
-
How about you read 'When should I vote down?' http://stackoverflow.com/help/privileges/vote-down and then explain to me how my answer was sloppy, no effort involved, or dangerously incorrect. – cnotethegr8 Nov 14 '15 at 15:46
-
Your answer is very thorough and you obviously put a lot of effort into it. However, using an undocumented API can lead to apps breaking out of nowhere, and more importantly, app store rejections. I'd call that dangerously incorrect. – Brian Sachetta Nov 15 '15 at 03:16
-
Again, the OP never requested a solution for the App Store! You are trying to modify the original question to excuse your actions. You can pretend you care about rules but your contradictory actions for those of stackoverflows are obvious. The lack of the OPs requesting code for the App Store or the use of only 'Public APIs' makes this answer not fall into the category of 'dangerously incorrect'. At least lower your ego and be honest with yourself. – cnotethegr8 Nov 15 '15 at 05:38
-
There's really no ego about it, man. People down vote things all the time - most of the time without even an explanation, sadly. I'm happy to remove the down vote if you mention in your post that the solution uses an undocumented API and is not guaranteed to work in the future / may not pass Apple's review process. That seems like a very fair compromise to me. – Brian Sachetta Nov 15 '15 at 14:48
-
I have no problem adding a note that it may break in the future, but I will not lie about it possibly not passing Apple's review process. Under no circumstances will this fail the review process. – cnotethegr8 Nov 16 '15 at 06:05
-
Agree to disagree on that last point but I will remove the downvote. – Brian Sachetta Nov 17 '15 at 13:38
-
1I'll throw in my unasked-for two cents. 1) Private views or view hierarchies are *not* private APIs. Apple has been making this distinction since forever. 2) Apple's guidelines are irrelevant to SO answers, unless the question clearly requires an answer acceptable to Apple. 3) It's a good idea to note that an answer may not be acceptable, but it's probably not worth a downvote. Simply leave a comment and move on. – Avi Mar 01 '16 at 11:14
I have the same problem and solved it by creating a little category that replace the BadgeView with an UILabel that you can customize easily.

- 324
- 4
- 12
-
1
-
1
-
You are accessing private property inside there. if([str isEqualToString:@"_UIBadgeView"] . How come Apple does not reject it? Did the permissions change? – Mantas Laurinavičius Sep 06 '16 at 12:41
-
For people using Swift, I managed to improve on TimWhiting answer in order to have the badge view working on any screen size and any orientation.
extension UITabBarController {
func setBadges(badgeValues: [Int]) {
for view in self.tabBar.subviews {
if view is CustomTabBadge {
view.removeFromSuperview()
}
}
for index in 0...badgeValues.count-1 {
if badgeValues[index] != 0 {
addBadge(index, value: badgeValues[index], color:UIColor(paletteItem: .Accent), font: UIFont(name: Constants.ThemeApp.regularFontName, size: 11)!)
}
}
}
func addBadge(index: Int, value: Int, color: UIColor, font: UIFont) {
let badgeView = CustomTabBadge()
badgeView.clipsToBounds = true
badgeView.textColor = UIColor.whiteColor()
badgeView.textAlignment = .Center
badgeView.font = font
badgeView.text = String(value)
badgeView.backgroundColor = color
badgeView.tag = index
tabBar.addSubview(badgeView)
self.positionBadges()
}
override public func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.positionBadges()
}
// Positioning
func positionBadges() {
var tabbarButtons = self.tabBar.subviews.filter { (view: UIView) -> Bool in
return view.userInteractionEnabled // only UITabBarButton are userInteractionEnabled
}
tabbarButtons = tabbarButtons.sort({ $0.frame.origin.x < $1.frame.origin.x })
for view in self.tabBar.subviews {
if view is CustomTabBadge {
let badgeView = view as! CustomTabBadge
self.positionBadge(badgeView, items:tabbarButtons, index: badgeView.tag)
}
}
}
func positionBadge(badgeView: UIView, items: [UIView], index: Int) {
let itemView = items[index]
let center = itemView.center
let xOffset: CGFloat = 12
let yOffset: CGFloat = -14
badgeView.frame.size = CGSizeMake(17, 17)
badgeView.center = CGPointMake(center.x + xOffset, center.y + yOffset)
badgeView.layer.cornerRadius = badgeView.bounds.width/2
tabBar.bringSubviewToFront(badgeView)
}
}
class CustomTabBadge: UILabel {}

- 4,898
- 1
- 31
- 43
No you can't change the color but you can use your own badges instead. Add this extension at the file scope and you can customise the badges however you like. Just call self.tabBarController!.setBadges([1,0,2])
in any of your root view controllers.
To be clear that is for a tab bar with three items, with the badge values going from left to right.
extension UITabBarController {
func setBadges(badgeValues:[Int]){
var labelExistsForIndex = [Bool]()
for value in badgeValues {
labelExistsForIndex.append(false)
}
for view in self.tabBar.subviews {
if view.isKindOfClass(PGTabBadge) {
let badgeView = view as! PGTabBadge
let index = badgeView.tag
if badgeValues[index]==0 {
badgeView.removeFromSuperview()
}
labelExistsForIndex[index]=true
badgeView.text = String(badgeValues[index])
}
}
for var i=0;i<labelExistsForIndex.count;i++ {
if labelExistsForIndex[i] == false {
if badgeValues[i] > 0 {
addBadge(i, value: badgeValues[i], color:UIColor(red: 4/255, green: 110/255, blue: 188/255, alpha: 1), font: UIFont(name: "Helvetica-Light", size: 11)!)
}
}
}
}
func addBadge(index:Int,value:Int, color:UIColor, font:UIFont){
let itemPosition = CGFloat(index+1)
let itemWidth:CGFloat = tabBar.frame.width / CGFloat(tabBar.items!.count)
let bgColor = color
let xOffset:CGFloat = 12
let yOffset:CGFloat = -9
var badgeView = PGTabBadge()
badgeView.frame.size=CGSizeMake(17, 17)
badgeView.center=CGPointMake((itemWidth * itemPosition)-(itemWidth/2)+xOffset, 20+yOffset)
badgeView.layer.cornerRadius=badgeView.bounds.width/2
badgeView.clipsToBounds=true
badgeView.textColor=UIColor.whiteColor()
badgeView.textAlignment = .Center
badgeView.font = font
badgeView.text = String(value)
badgeView.backgroundColor = bgColor
badgeView.tag=index
tabBar.addSubview(badgeView)
}
}
class PGTabBadge: UILabel {
}

- 2,405
- 5
- 21
- 41
Swift 3 Here is an updated version of @Kirualex's answer (who improved on @TimWhiting's answer) for Swift 3.
extension UITabBarController {
func setBadges(badgeValues: [Int]) {
for view in self.tabBar.subviews {
if view is CustomTabBadge {
view.removeFromSuperview()
}
}
for index in 0...badgeValues.count-1 {
if badgeValues[index] != 0 {
addBadge(index: index, value: badgeValues[index], color: UIColor.blue, font: UIFont(name: "Helvetica-Light", size: 11)!)
}
}
}
func addBadge(index: Int, value: Int, color: UIColor, font: UIFont) {
let badgeView = CustomTabBadge()
badgeView.clipsToBounds = true
badgeView.textColor = UIColor.white
badgeView.textAlignment = .center
badgeView.font = font
badgeView.text = String(value)
badgeView.backgroundColor = color
badgeView.tag = index
tabBar.addSubview(badgeView)
self.positionBadges()
}
override open func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.positionBadges()
}
// Positioning
func positionBadges() {
var tabbarButtons = self.tabBar.subviews.filter { (view: UIView) -> Bool in
return view.isUserInteractionEnabled // only UITabBarButton are userInteractionEnabled
}
tabbarButtons = tabbarButtons.sorted(by: { $0.frame.origin.x < $1.frame.origin.x })
for view in self.tabBar.subviews {
if view is CustomTabBadge {
let badgeView = view as! CustomTabBadge
self.positionBadge(badgeView: badgeView, items:tabbarButtons, index: badgeView.tag)
}
}
}
func positionBadge(badgeView: UIView, items: [UIView], index: Int) {
let itemView = items[index]
let center = itemView.center
let xOffset: CGFloat = 12
let yOffset: CGFloat = -14
badgeView.frame.size = CGSize(width: 17, height: 17)
badgeView.center = CGPoint(x: center.x + xOffset, y: center.y + yOffset)
badgeView.layer.cornerRadius = badgeView.bounds.width/2
tabBar.bringSubview(toFront: badgeView)
}
}
class CustomTabBadge: UILabel {}

- 1,103
- 14
- 23
It appears that no. You may only set the value. From Apple's documentation badge is:
Text that is displayed in the upper-right corner of the item with a surrounding red oval.

- 2,097
- 2
- 15
- 14
Since iOS 15 has different approach, what worked in my case:
let appearance = UITabBarAppearance()
appearance.configureWithTransparentBackground()
let barAppearance = UITabBarItemAppearance()
barAppearance.normal.badgeBackgroundColor = .green
barAppearance.normal.badgeTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.red]
appearance.stackedLayoutAppearance = barAppearance
tabBar.standardAppearance = appearance

- 297
- 3
- 6
-
2Note that in addition to `normal` there are also `selected` and `focused` states in `UITabBarItemAppearance`. – Jason Moore Mar 14 '22 at 07:02
You need to specify tab item at index to change badge color, #available in iOS 10 ,
if #available(iOS 10.0, *)
{
self.kAppTabBarController.tabBar.items![1].badgeColor = YOUR_COLOR
}

- 277
- 2
- 8
You can now do it in the storyboard too, by selecting your tab bar item and going to the attributes inspector.

- 5,679
- 5
- 48
- 69
YES, But the only possible solution is to create a custom Tabbar and creating your custom tabbar badge icon. You will find many article/code for creating custom tabbar.

- 2,195
- 4
- 32
- 61
// change TabBar BadgeView background Color
-(void)changeTabBarBadgeViewBgColor:(UITabBar*)tabBar {
for (UIView* tabBarButton in tabBar.subviews) {
for (UIView* badgeView in tabBarButton.subviews) {
NSString* className = NSStringFromClass([badgeView class]);
// looking for _UIBadgeView
if ([className rangeOfString:@"BadgeView"].location != NSNotFound) {
for (UIView* badgeSubview in badgeView.subviews) {
NSString* className = NSStringFromClass([badgeSubview class]);
// looking for _UIBadgeBackground
if ([className rangeOfString:@"BadgeBackground"].location != NSNotFound) {
@try {
[badgeSubview setValue:nil forKey:@"image"];
[badgeSubview setBackgroundColor:[UIColor blueColor]];
badgeSubview.clipsToBounds = YES;
badgeSubview.layer.cornerRadius = badgeSubview.frame.size.height/2;
}
@catch (NSException *exception) {}
}
if ([badgeSubview isKindOfClass:[UILabel class]]) {
((UILabel *)badgeSubview).textColor = [UIColor greenColor];
}
}
}
}
}
}

- 1,196
- 1
- 18
- 31
Hm...it's very easy. [[self tabBarItem] setBadgeColor:[UIColor greenColor]];

- 1,115
- 1
- 16
- 29
Add below lines of code in UITabBarController :
class RootTabBarViewController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
if #available(iOS 13.0, *) {
let appearance = tabBar.standardAppearance.copy()
setTabBarItemBadgeAppearance(appearance.stackedLayoutAppearance)
setTabBarItemBadgeAppearance(appearance.inlineLayoutAppearance)
setTabBarItemBadgeAppearance(appearance.compactInlineLayoutAppearance)
tabBar.standardAppearance = appearance
if #available(iOS 15.0, *) {
tabBar.scrollEdgeAppearance = appearance
}
}
// Do any additional setup after loading the view.
}
@available(iOS 13.0, *)
private func setTabBarItemBadgeAppearance(_ itemAppearance: UITabBarItemAppearance) {
itemAppearance.normal.badgeBackgroundColor = UIColor.colorBlue207DFF
}
}

- 4,293
- 3
- 25
- 51
Since iOS 15 / Xcode 13, you have to set stackedLayoutAppearance property to change badge color on UITabBarItem. Change just ".blue" with you own color:
if #available(iOS 15.0, *) {
let appearance = UITabBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.stackedLayoutAppearance.normal.badgeBackgroundColor = .blue
UITabBar.appearance().standardAppearance = appearance
UITabBar.appearance().scrollEdgeAppearance = appearance
}
Tested on Xcode 14.1 / iOS 16.

- 2,656
- 23
- 16
Take a look here @ UITabbarItem-CustomBadge.
A complete demonstration is following
it takes only two line of code, if you want to use the default implementation
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//supplying the animation parameter
[UITabBarItem setDefaultAnimationProvider:[[DefaultTabbarBadgeAnimation alloc] init]];
[UITabBarItem setDefaultConfigurationProvider:[[DefaultSystemLikeBadgeConfiguration alloc] init]];
//rest of your code goes following...
return YES;
}

- 7,484
- 4
- 35
- 44