Customizing the navigation bar in UIKit

What worked for me and what didn’t.

Published: April 4, 2022
I want a Press Kit

This post will be pretty different from the others. I usually strive to present the cleanest solution, but with UINavigationBar, if you want to customize everything, you will have to live with a strange code.

Anyway, I recently wanted to customize every color aspect of the navigation bar and dynamically toggle it based on the selected theme in the app. This is because some colors need dark text for sufficient contrast.

Here are the things I wanted to customize and we will cover in this post:

  • Navigation bar background
  • Navigation title color
  • Back button color
  • Bar buttons color
  • Status bar style (either dark or white)

I wanted to use the appearance API introduced with iOS 13 for all of this but had to resort to other methods to customize the navbar fully.

Before we start, I want to emphasize that these most likely aren’t the best practices for this type of customization, but it has been the only way that worked for me on iOS 15, so please keep this in mind.

I also want to mention that there is a global appearance API. Still, I am not a fan of those for any larger customizations, because you lose control and can inadvertently change system screens that you use inside your app.

Let’s go over the customizations one by one. **

How to change navigation bar background

Changing the background is fortunately pretty straightforward. We will be using the UINavigationBarAppearance API that was introduced with iOS 13.

The workflow is to create a new appearance object, customize its properties and then use it to modify your navigation bar.

Since iOS 15, navbars default to transparent background with no content behind them. I have covered this behavior previously. To be more precise, before iOS 15 this transparent appearance was used by default for the large titles mode.

let navigationBarAppearance = UINavigationBarAppearance()
navigationBarAppearance.configureWithDefaultBackground()
navigationBarAppearance.backgroundColor = .systemBlue

navigationItem.standardAppearance = navigationBarAppearance
navigationItem.compactAppearance = navigationBarAppearance
navigationItem.scrollEdgeAppearance = navigationBarAppearance

We start by creating the appearance object, then use one of the basic configuration methods, and finally customize the background.

I want the navigation bar always to appear the same, so I am using this appearance for all three possible options.

The next customizations will be similar. I will keep using the entire code in case you want to copy and paste it to your project quickly, even though the changes will be pretty small.

How to change navigation title color

Customizing the title color is done similarly to the background. And since this is based on NSAttributedString, you can also change the font used and other text attributes.

let navigationBarAppearance = UINavigationBarAppearance()
navigationBarAppearance.configureWithDefaultBackground()
navigationBarAppearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]

navigationItem.standardAppearance = navigationBarAppearance
navigationItem.compactAppearance = navigationBarAppearance
navigationItem.scrollEdgeAppearance = navigationBarAppearance

And if you are using the large titles, you need to modify also the largeTitleTextAttributes.

How to change the back button color

This is where the “fun” starts. I couldn’t use the above appearance to change the color used for the back button with the back title.

What helped here was configuring the tint color on the navigation bar itself.

navigationController?.navigationBar.tintColor = .white

How to change bar buttons color

Yet another customization I needed to do was to change the colours of the UIBarButton items in the navbar.

In this case I changed the tint colours directly like this:

navigationItem.leftBarButtonItem?.tintColor = .white
navigationItem.rightBarButtonItem?.tintColor = .white

How to change a status bar style

Customizing the status bar took me the longest time to figure out. View controllers have the property preferredStatusBarStyle you can set or override to customize the status bar's style (the clock, signal indicator, and other icons), but this doesn’t work with UINavigationController.

The navigation controller has its property, and even setting it directly didn’t work for me.

So, in the end, I created a custom subclass with the option to override the value there:

import UIKit

class CustomNavigationController: UINavigationController {
    var overrideStatusBarStyle: UIStatusBarStyle? {
        didSet {
            setNeedsStatusBarAppearanceUpdate()
        }
    }

    override var preferredStatusBarStyle: UIStatusBarStyle {
        if let overrideStatusBarStyle = overrideStatusBarStyle {
            return overrideStatusBarStyle
        } else {
            return super.preferredStatusBarStyle
        }
    }
}

And this works as expected for me.

The usage is a bit convoluted but nothing too bad:

(navigationController as? CustomNavigationController)?.overrideStatusBarStyle = .lightContent

And also to reset it:

(navigationController as? CustomNavigationController)?.overrideStatusBarStyle = nil

However, I had to call this in the viewDidAppear method on the screen where I wanted to reset the status bar style for it to work correctly.

This is probably the least clean solution in the article however I am sticking with it for now because I want to develop more exciting problems than trying to change the status bar color 🤷

Bluesky logo

Follow on Bluesky to not miss new posts

Filip Němeček profile photo

WRITTEN BY

Filip Němeček Mastodon

iOS blogger and developer with interest in Python/Django.

iOS blogger and developer with interest in Python/Django.