Is it possible to customize UITabBarItem Badge?
Solution 1
This is your best bet. 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.
If you want to add images instead just change the addBadge
method
extension UITabBarController {
func setBadges(badgeValues: [Int]) {
var labelExistsForIndex = [Bool]()
for _ in badgeValues {
labelExistsForIndex.append(false)
}
for view in self.tabBar.subviews where view is 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 i in 0...(labelExistsForIndex.count - 1) where !labelExistsForIndex[i] && (badgeValues[i] > 0) {
addBadge(index: i, value: badgeValues[i], color: .red, 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 = 5
let yOffset: CGFloat = -12
let badgeView = PGTabBadge()
badgeView.frame.size = CGSize(width: 12, height: 12)
badgeView.center = CGPoint(x: (itemWidth * itemPosition) - (itemWidth / 2) + xOffset, y: 20 + yOffset)
badgeView.layer.cornerRadius = badgeView.bounds.width/2
badgeView.clipsToBounds = true
badgeView.textColor = UIColor.white
badgeView.textAlignment = .center
badgeView.font = font
badgeView.text = String(value)
badgeView.backgroundColor = bgColor
badgeView.tag = index
tabBar.addSubview(badgeView)
}
}
class PGTabBadge: UILabel { }
Solution 2
Here is another solution based on TimWhiting's answer:
extension UITabBar {
func setBadge(value: String?, at index: Int, withConfiguration configuration: TabBarBadgeConfiguration = TabBarBadgeConfiguration()) {
let existingBadge = subviews.first { ($0 as? TabBarBadge)?.hasIdentifier(for: index) == true }
existingBadge?.removeFromSuperview()
guard let tabBarItems = items,
let value = value else { return }
let itemPosition = CGFloat(index + 1)
let itemWidth = frame.width / CGFloat(tabBarItems.count)
let itemHeight = frame.height
let badge = TabBarBadge(for: index)
badge.frame.size = configuration.size
badge.center = CGPoint(x: (itemWidth * itemPosition) - (0.5 * itemWidth) + configuration.centerOffset.x,
y: (0.5 * itemHeight) + configuration.centerOffset.y)
badge.layer.cornerRadius = 0.5 * configuration.size.height
badge.clipsToBounds = true
badge.textAlignment = .center
badge.backgroundColor = configuration.backgroundColor
badge.font = configuration.font
badge.textColor = configuration.textColor
badge.text = value
addSubview(badge)
}
}
class TabBarBadge: UILabel {
var identifier: String = String(describing: TabBarBadge.self)
private func identifier(for index: Int) -> String {
return "\(String(describing: TabBarBadge.self))-\(index)"
}
convenience init(for index: Int) {
self.init()
identifier = identifier(for: index)
}
func hasIdentifier(for index: Int) -> Bool {
let has = identifier == identifier(for: index)
return has
}
}
class TabBarBadgeConfiguration {
var backgroundColor: UIColor = .red
var centerOffset: CGPoint = .init(x: 12, y: -9)
var size: CGSize = .init(width: 17, height: 17)
var textColor: UIColor = .white
var font: UIFont! = .systemFont(ofSize: 11) {
didSet { font = font ?? .systemFont(ofSize: 11) }
}
static func construct(_ block: (TabBarBadgeConfiguration) -> Void) -> TabBarBadgeConfiguration {
let new = TabBarBadgeConfiguration()
block(new)
return new
}
}
Solution 3
You can use a more robust solution @UITabbarItem-CustomBadge.
Demo
Simple two line of code can get you going
- (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;
}
Solution 4
TimWhiting's answer updated to Swift 4 with the removal of some force unwrapping.
extension UITabBarController {
func setBadges(badgeValues: [Int]) {
var labelExistsForIndex = [Bool]()
for _ in badgeValues {
labelExistsForIndex.append(false)
}
for view in tabBar.subviews {
if let badgeView = view as? PGTabBadge {
let index = badgeView.tag
if badgeValues[index] == 0 {
badgeView.removeFromSuperview()
}
labelExistsForIndex[index] = true
badgeView.text = String(badgeValues[index])
}
}
for i in 0 ... labelExistsForIndex.count - 1 where !labelExistsForIndex[i] && badgeValues[i] > 0 {
addBadge(
index: 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) {
guard let tabBarItems = tabBar.items else { return }
let itemPosition = CGFloat(index + 1)
let itemWidth: CGFloat = tabBar.frame.width / CGFloat(tabBarItems.count)
let bgColor = color
let xOffset: CGFloat = 12
let yOffset: CGFloat = -9
let badgeView = PGTabBadge()
badgeView.frame.size = CGSize(width: 17, height: 17)
badgeView.center = CGPoint(x: (itemWidth * itemPosition) - (itemWidth / 2) + xOffset, y: 20 + yOffset)
badgeView.layer.cornerRadius = badgeView.bounds.width / 2
badgeView.clipsToBounds = true
badgeView.textColor = UIColor.white
badgeView.textAlignment = .center
badgeView.font = font
badgeView.text = String(value)
badgeView.backgroundColor = bgColor
badgeView.tag = index
tabBar.addSubview(badgeView)
}
}
class PGTabBadge: UILabel {}
Sample Image
Solution 5
well... changing the background of the built-in badge does not seem possible to me. But what IS possible instead, is the following:
Subclass the TabBarController, build a custom view laid out in a NIB, add it to the view hierarchy at the location in the tab bar, where you want it to be.
In the custom view you can set up an image as background and a label on top of that background, that will display the number value you can then change by code.
Then you need to figure out the horizontal and vertical location of your custom view where you want to place your view inside of the tab bar.
Hope this helps a little bit. Sebastian
Michelle Ybanez
Updated on July 17, 2022Comments
-
Michelle Ybanez almost 2 years
The question below is similar as mine.
How to use a custom UIImage as an UITabBarItem Badge?
Is it possible to use a background image instead of drawing it myself? I think it's fine since my app will only use a 1-digit badgeValue.
If it is not really possible, I want to know if we can change the badge color instead. The answers in the question below are not really helping.
-
Michelle Ybanez about 11 yearsThank you for this idea. I will try this out. I have a follow-up question though. Is this badge customization alright if this app will be submitted to the AppStore?
-
sesc360 about 11 yearsYou are welcome... please be so kind and accept the answer if you are satisfied. Well.... this is no problem because there is no need to use the badge provided by apple if you don't like it. You only get into trouble if you try do modify private classes provided by apple.
-
Fábio Oliveira over 8 yearsIs this App Store safe?
-
TimWhiting over 8 yearsAbsolutely, the tab bar is just a view, so adding subviews to it is totally legit.
-
Ratul Sharker over 7 yearsplease leave a reason in comment if anyone gonna leave a -1
-
Ratul Sharker over 7 yearsby the way its a github repository of my own, should i add full 6 files of code ? are you okay with that ? @Zoleas
-
Jesse S. about 5 yearsThanks! This isn't removing the badge though once the tab bar item with the badge is tapped. Any help would be appreciated. :)
-
Widerberg almost 5 years@JesseS. You can manually remove it upon tapping by setting the badge's value to nil.
-
Kacy about 3 yearsYou're a legend.