In my last post, I went over customizing your NavigationView’s bar using the UINavigationBarAppearance API. Considering how similar a NavigationView and TabView are, it seems only natural to talk about customizing your TabBar using UIAppearance and UITabBarAppearance in this post.

The old way: UIAppearance

Similar to UINavigationBar, I’d recommend using this API in OnAppear. Since appearance() returns a static instance of UIAppearance, changes here will be global. The base code for setting the background color looks like:

struct ContentView: View { @State private var selection = 0 var body: some View { TabView(selection: $selection){ Text("First View") .font(.title) .tabItem { Text("First") } .tag(0) Text("Second View") .font(.title) .tabItem { Text("Second") } .tag(1) } .onAppear() { UITabBar.appearance().backgroundColor = .red } } }

There are some oddities with this.

First, setting the background color changes more than just the background. If you look closely, you’ll notice that the non-selected text’s color was also changed. This can be overridden using unselectedItemTintColor.

Second, there’s other properties you can set, like tintColor, that don’t really work. I found out that to set the color on the selected tab, you need to set the accentColor. My guess is that there is always an accentColor that’s being applied, and that overwrites the tintColor.

struct ContentView: View { @State private var selection = 0 var body: some View { TabView(selection: $selection){ Text("First View") .font(.title) .tabItem { Text("First") } .tag(0) Text("Second View") .font(.title) .tabItem { Text("Second") } .tag(1) } .onAppear() { UITabBar.appearance().backgroundColor = .red } .accentColor(.red) } }

This is way less flexible than the UIAppearance for NavigationViews. Let’s see if UITabVarAppearance is any better.

The New Way: UITabBarAppearance

Again, there’s no way to pass in a specific UITabBarAppearance to your TabView, so you have to extend the UITabBarController to use it. The base code is going to look something like the following:

extension UITabBarController { override open func viewDidLoad() { super.viewDidLoad() let standardAppearance = UITabBarAppearance() // update the appearance tabBar.standardAppearance = standardAppearance } }

As with UIAppearance, some stuff stuff doesn’t work. Let’s first go over stuff that works, then we can go over stuff that doesn’t.

Things That Work

First and foremost, background, shadowColor and backgroundImage still work as you’d expect:

extension UITabBarController { override open func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let standardAppearance = UITabBarAppearance() standardAppearance.backgroundColor = .red standardAppearance.shadowColor = .green standardAppearance.backgroundImage = UIImage(named: "texture") tabBar.standardAppearance = standardAppearance } }

configureWithTransparentBackground and configureWithOpaqueBackground both work as well:

extension UITabBarController { override open func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let standardAppearance = UITabBarAppearance() standardAppearance.configureWithOpaqueBackground() standardAppearance.configureWithTransparentBackground() tabBar.standardAppearance = standardAppearance } }

stackedItemPositioning, stackedItemSpacing, and stackedItemWidth all seem to work too. Note: stackedItemSpacing and stackedItemWidth only work when stackedItemPositioning is set to .centered.

extension UITabBarController { override open func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let standardAppearance = UITabBarAppearance() standardAppearance.stackedItemPositioning = .centered standardAppearance.stackedItemSpacing = 30 standardAppearance.stackedItemWidth = 30 standardAppearance.stackedItemPositioning = .fill standardAppearance.stackedItemSpacing = 30 standardAppearance.stackedItemWidth = 30 tabBar.standardAppearance = standardAppearance } }

Things That Don’t Work

One thing I found that didn’t work is the selectionIndicatorTintColor. The following code does nothing.

extension UITabBarController { override open func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let standardAppearance = UITabBarAppearance() standardAppearance.selectionIndicatorTintColor = .red tabBar.standardAppearance = standardAppearance } }

However, you can still update the tint color if you update the accentColor like with UIAppearance. You also get more granular control over the colors using UITabBarItemAppearance, which I’ll explain below.

The 3 UITabBarItemAppearances

Right around the 8 minute mark in the WWDC video, apple mentions there are 3 different appearances for UITabBarAppearance:

stackedLayoutAppearance – This appears to be the standard appearance

inlineLayoutAppearance – “Which you’ll see on iPads”

compactInlineLayoutAppearance – “Which you’ll see on smaller phones”

These 3 appearance are used differently than the ones for UINavigationViewAppearance. They’re a property of the StandardAppearance, not of a UITabBar. To update them, you do something like:

extension UITabBarController { override open func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let standardAppearance = UITabBarAppearance() let tabBarItemAppearance = UITabBarItemAppearance() standardAppearance.inlineLayoutAppearance = tabBarItemAppearance standardAppearance.stackedLayoutAppearance = tabBarItemAppearance standardAppearance.compactInlineLayoutAppearance = tabBarItemAppearance tabBar.standardAppearance = standardAppearance } }

The WWDC video didn’t make it clear when these are used, so I wrote some code to help me figure this out.

extension UITabBarController { override open func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let standardAppearance = UITabBarAppearance() standardAppearance.stackedLayoutAppearance.focused.titleTextAttributes = [.foregroundColor: UIColor.red] standardAppearance.stackedLayoutAppearance.normal.titleTextAttributes = [.foregroundColor: UIColor.red] standardAppearance.stackedLayoutAppearance.selected.titleTextAttributes = [.foregroundColor: UIColor.red] standardAppearance.inlineLayoutAppearance.focused.titleTextAttributes = [.foregroundColor: UIColor.green] standardAppearance.inlineLayoutAppearance.normal.titleTextAttributes = [.foregroundColor: UIColor.green] standardAppearance.inlineLayoutAppearance.selected.titleTextAttributes = [.foregroundColor: UIColor.green] standardAppearance.compactInlineLayoutAppearance.focused.titleTextAttributes = [.foregroundColor: UIColor.blue] standardAppearance.compactInlineLayoutAppearance.normal.titleTextAttributes = [.foregroundColor: UIColor.blue] standardAppearance.compactInlineLayoutAppearance.selected.titleTextAttributes = [.foregroundColor: UIColor.blue] tabBar.standardAppearance = standardAppearance } }

After some more playing around, I figured out that .normal changes the non-selected tint color and .selected changes selected tint color. That said, I couldn’t get .focused or .disabled to change anything.

Conclusions

When I first started playing around with TabViews, I was a little annoyed. The API is very unintuitive, and I couldn’t find resources for it. You also get a lot of customization than you really need (Who wants different colors for compact vs inline?).

However, this new API is powerful. It’s not totally there yet, but it’s a good start.

Feel free to checkout my YouTube tutorial on TabBar customization.

Thanks for reading.

– SchwiftyUI