Using UITabBarAppearance for Tab Bar changes in iOS 13

With iOS 13, Apple finally made some long overdue changes to support more effective customizations of the tab bars, and tool bars in your apps. The new UITabBarAppearance, UIBarAppearance, and related classes lets developers easily modify not just the basics like background and selected colors but spacing, text attributes, badging, and positioning. Creating subtle effects around tab bar item selection that would have been non-trivial in the past is much simpler now.

The Old Way

In iOS 12, you might have done something like the following to set the background color and selected/unselected tab bar item colors.

    //Set the background color
    UITabBar.appearance().backgroundColor = .red
    tabBar.backgroundImage = UIImage()   //Clear background

    //Set the item tint colors
    tabBar.tintColor = .white
    tabBar.unselectedItemTintColor = .lightGray

Using UIAppearance to set global properties such as background color worked well but no longer works as consistently with iOS 13 (although it still will generally work). I’ve noticed issues in particular with flaky tab bar appearance on iPad within UISplitViewController classes and some other scenarios in iOS 13.

New and Improved!

With the new classes in iOS 13 you now set the tab bar background color with the UITabBarAppearance class and backgroundColor property. Note the availability check on the method below as this is only available in iOS 13+. (if you are not supporting older versions of iOS, this obviously wouldn’t be needed). Here we just set the tab bar background to red and assign the standard appearance.

    //This example assumes a custom UITabBarController overridden class.  You can 
    //also use it anywhere the `tabBar` of the current `tabBarController` is available.
    let appearance = UITabBarAppearance()
    appearance.backgroundColor = .red
    tabBar.standardAppearance = appearance

Setting the color on the selected and unselected tab bar icons and text is now done through the new UITabBarItemAppearance class. You can then just use the default normal, selected, disabled, or focused properties that are available and already instantiated to set iconColor, titleTextAttributes, or other properties. Below is a function to update the colors for normal and selected tab bar items.

    @available(iOS 13.0, *)
    private func setTabBarItemColors(_ itemAppearance: UITabBarItemAppearance) {
        itemAppearance.normal.iconColor = .lightGray
        itemAppearance.normal.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.lightGray]
        
        itemAppearance.selected.iconColor = .white
        itemAppearance.selected.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
    }

We can just call the above function for each appropriate appearance – stacked, inline, or compact inline. iOS 13 will generally select the layout most appropriate to the size class that you select (e.g. stacked for portrait iPhone view and inline for landscape iPhone view on an iPhone 11 Pro Max).

    //Set all possible tab bar item styles as necessary (based on rotation and size capabilities).  Here
    //we're setting all three available appearances
    setTabBarItemColors(appearance.stackedLayoutAppearance)
    setTabBarItemColors(appearance.inlineLayoutAppearance)
    setTabBarItemColors(appearance.compactInlineLayoutAppearance)

This will result in a UI similar to the following for landscape or portrait representations:

Landscape view of tab bar
Portrait view of tab bar

Notice the differing stacked and inline appearances based on the iPhone rotation above. If you want to adjust other characteristics such as the title position (say to move it further or closer to the icon), font, etc., it’s simple enough to do with the titleTextAttributes and titlePositionAdjustment properties.

Setting Tab Bar Item Badge Appearance

In the screenshot below we’ve got a blue badge instead of the default red one and it’s been moved so that it overlaps the top of the navigation bar.

Alternative badge style for tab bar item

To do this we just set the badgeBackgroundColor, badgeTextAttributes, and badgePositionAdjustment properties on UITabBarItemAppearance such as below:

    
    @available(iOS 13.0, *)
    private func setTabBarItemBadgeAppearance(_ itemAppearance: UITabBarItemAppearance) {
        //Adjust the badge position as well as set its color
        itemAppearance.normal.badgeBackgroundColor = .blue
        itemAppearance.normal.badgeTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
        itemAppearance.normal.badgePositionAdjustment = UIOffset(horizontal: 10, vertical: -10)
    }

Conclusion

I hope you’ve enjoyed the above and learned something about the new tab bar appearance classes. Make sure to check out the documentation at the links above as well as the other derived UIBarAppearance classes such as UINavigationBarAppearance and UIToolBarAppearance. Hopefully some of the ugly subclassing and iterative loops through nav bars, tab bars, and tool bars is a thing of the past!

One thought on “Using UITabBarAppearance for Tab Bar changes in iOS 13

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: