UITabBar change background color of one UITabBarItem on iOS7

Viewed 9362

Can I change the background color of a specific UITabBarItem in a UITabBar?

I know how to change all the background of the selected background, using:

[[UITabBar appearance] setBarTintColor:[UIColor redColor]];
[[UITabBar appearance] setTintColor:[UIColor blueColor]];
[[UITabBar appearance] setSelectedImageTintColor:[UIColor yellowColor]];

But can it be done for only one item without subclassing?

Thanks

6 Answers

Updated for Swift 4:

let itemIndex: CGFloat = 2.0
let bgColor = UIColor.ownBlue

let itemWidth = tabBar.frame.width / CGFloat(tabBar.items!.count)
let bgView = UIView(frame: CGRect(x: itemWidth * itemIndex, y: 0, width: itemWidth, height: tabBar.frame.height))

bgView.backgroundColor = bgColor
tabBar.insertSubview(bgView, at: 0)

You cannot really do this with the tint color property. See this post about the fact that even the setSelectedImageTintColor does not really seem to be working (it did not last time I checked).

The solution is to change the tintColor on the fly for the item in question. You can do this ad hoc whenever the selected item changes by implementing the UITabBarDelegate method tabBar:didSelectItem: e.g. in the app delegate.

Late to the game, but this is how I did it. I made the tabs look like buttons, with rounded views. The answers above, have issues when the device is rotated, or the app is sent into a split window.

This solution marries the background directly to each tab, using auto-layout, so they naturally follow the tab bar changes.

This is done on the custom subclass of UITabBarController.

First, I set the colors (white for unselected, and black for selected), of the icons in the tab items, then, I iterate the tabs, and insert backgrounds to each, using auto layout to have them cleave to the tabs:

override func viewDidLoad() {
    super.viewDidLoad()
    let appearance = UITabBarAppearance()
    appearance.stackedLayoutAppearance.normal.iconColor = .white
    appearance.stackedLayoutAppearance.selected.iconColor = .black
    appearance.inlineLayoutAppearance.normal.iconColor = .white
    appearance.inlineLayoutAppearance.selected.iconColor = .black
    appearance.compactInlineLayoutAppearance.normal.iconColor = .white
    appearance.compactInlineLayoutAppearance.selected.iconColor = .black

    tabBar.subviews.forEach {
        if let item = $0 as? UIControl {
            let wrapper = UIView()
            wrapper.isUserInteractionEnabled = false
            wrapper.clipsToBounds = true
            wrapper.backgroundColor = tabBar.tintColor
            wrapper.layer.cornerRadius = 8
            item.insertSubview(wrapper, at: 0)
            wrapper.translatesAutoresizingMaskIntoConstraints = false
            wrapper.leadingAnchor.constraint(equalTo: item.leadingAnchor).isActive = true
            wrapper.trailingAnchor.constraint(equalTo: item.trailingAnchor).isActive = true
            wrapper.topAnchor.constraint(equalTo: item.topAnchor).isActive = true
            wrapper.bottomAnchor.constraint(equalTo: item.bottomAnchor).isActive = true
        }
    }
}

The reason this works, is that the tab bar implements the tab items as an internal subclass of UIControl, which is a UIView.

I can add views underneath the control.

This ends up looking like this:

Sample Tab Bar

NB: Turning off user interaction is important, as is inserting at 0.

The drawback is that there's no way to tell which item is the selected one (at the time the background images are created).

ALSO NB: Apple REALLY doesn't want us mucking about in the tab bar, so I could see this method (as well as the ones above), breaking in some future OS upgrade.

Related