1

How to add a custom border to the badge in the tab bar item? By default it shows a red dot in the top right corner of the tab bar item. I want to add a border to it. There is an option to change the color of the badge but I could not find anything to add a border to the badge.

rmaddy
  • 314,917
  • 42
  • 532
  • 579

1 Answers1

1

I don't think there is an official way of adding a border to the badge, but I also wanted to do this and I ended up developing my own way. My solution is more like a hack, but it works on Xcode 10.1 and iOS 12.1 and will not get your app rejected, so maybe it's good enough for you.

The hardest part is to actually get a hold of the badge, this is the view hierarchy for the tab bar with 5 tabs:

enter image description here

You can see that in my project the last tab holds the badge. But actually it's the 4th tab (the one with index = 3) that has the badge, this is because I bring the 4th tab to top in my UITabBarController class so the badge is not clipped in case it displays bigger values. Here is how I get the badge view (this is in the UITabBar context, so self is UITabBar):

func badge(forIndex index: Int) -> UIView? {
    let sorted = self.subviews.sorted { (view1, view2) -> Bool in // I need to sort it by the frame.origin.x because the order in the subviews array cannot be trusted, I got random order there during my tests
        return view1.frame.origin.x < view2.frame.origin.x
    }
    let idx = index + 1 // the first view is actually a _UIBarBackground
    guard let barItemView = sorted[safe: idx] else {
        print("\(#file):\(#function):\(#line): Could not find a subview with index \(idx) in \(self)")
        return nil
    }
    if barItemView.subviews.count == 3 { // this is a hack, but I could not find a better way to get the badge without using private interfaces
        let view = barItemView.subviews.last
        return view
    }
    return nil
}

This gives you the badge view, and with the hardest (and ugliest) part behind, we can now add the border (or change the position as I do in my project):

if let badgeView = badge(forIndex: index) {
    // I found that it's easier to transform the layer here instead of the view, because it's instantaneous
    let badgeViewLayer = badgeView.layer
    badgeViewLayer.transform = CATransform3DMakeTranslation(BadgeTranslationX, BadgeTranslationY, 1) // you can change the position of the badge with this

    guard let badgeViewImageView = badgeView.subviews.first else {
        print("\(#file):\(#function):\(#line): No image view inside the badge view")
        return
    }

    badgeViewImageView.clipsToBounds = true // this is important, otherwise you may get artifacts when working with rounded corners etc.
    let layer = badgeViewImageView.layer
    layer.borderColor = UIColor.white.cgColor
    layer.borderWidth = 2
    layer.cornerRadius = 8
}

And here is what you get with the above code:

enter image description here

Of course, the color can be anything:

enter image description here

lawicko
  • 7,246
  • 3
  • 37
  • 49