iOS 13
status bar customization
background color
text color
iOS development

How to change the status bar background color and text color on iOS 13?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

On iOS 13, status bar styling is split into two separate concerns. The text and icon color are controlled by the active view controller, while the visible background color comes from the views behind the status bar area rather than from a direct public status-bar background API.

Controlling the Text and Icon Color

The supported way to change status bar content style is to override preferredStatusBarStyle in the relevant view controller.

swift
1import UIKit
2
3final class DetailsViewController: UIViewController {
4    private var useLightStatusBar = true
5
6    override var preferredStatusBarStyle: UIStatusBarStyle {
7        return useLightStatusBar ? .lightContent : .darkContent
8    }
9
10    func applyTheme(isDarkHeader: Bool) {
11        useLightStatusBar = isDarkHeader
12        setNeedsStatusBarAppearanceUpdate()
13    }
14}

Use .lightContent for dark backgrounds and .darkContent for light backgrounds on iOS 13 and newer. If the header theme changes while the screen is visible, call setNeedsStatusBarAppearanceUpdate() so UIKit asks the controller for the new style.

Creating the Background Color

There is no public property that directly sets a status bar background color. The standard workaround is to color the area behind the status bar by adding a top overlay view.

swift
1import UIKit
2
3final class DetailsViewController: UIViewController {
4    private let statusBarBackground = UIView()
5
6    override func viewDidLoad() {
7        super.viewDidLoad()
8
9        view.backgroundColor = .systemBackground
10        statusBarBackground.translatesAutoresizingMaskIntoConstraints = false
11        statusBarBackground.backgroundColor = .systemBlue
12
13        view.addSubview(statusBarBackground)
14
15        NSLayoutConstraint.activate([
16            statusBarBackground.topAnchor.constraint(equalTo: view.topAnchor),
17            statusBarBackground.leadingAnchor.constraint(equalTo: view.leadingAnchor),
18            statusBarBackground.trailingAnchor.constraint(equalTo: view.trailingAnchor),
19            statusBarBackground.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor)
20        ])
21    }
22}

This approach is stable because it uses normal layout rules instead of relying on private UIKit internals.

If your screen is inside a navigation controller, the navigation controller may be the object UIKit consults for status bar style. In that case, the child screen's override is ignored unless the container forwards it.

swift
1import UIKit
2
3final class AppNavigationController: UINavigationController {
4    override var childForStatusBarStyle: UIViewController? {
5        return topViewController
6    }
7}

Once the navigation controller forwards style decisions, the top screen can control the text color as expected.

To make the whole top region look consistent, style the navigation bar too.

swift
1let appearance = UINavigationBarAppearance()
2appearance.configureWithOpaqueBackground()
3appearance.backgroundColor = .systemBlue
4appearance.titleTextAttributes = [.foregroundColor: UIColor.white]
5
6navigationController?.navigationBar.standardAppearance = appearance
7navigationController?.navigationBar.scrollEdgeAppearance = appearance
8navigationController?.navigationBar.tintColor = .white

Supporting Dark Mode

iOS 13 introduced dark mode, which means hard-coded colors can quickly become unreadable. A dark overlay plus dark status bar text is an obvious contrast bug.

A simple way to react is to update the background when traits change.

swift
1override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
2    super.traitCollectionDidChange(previousTraitCollection)
3
4    let darkMode = traitCollection.userInterfaceStyle == .dark
5    statusBarBackground.backgroundColor = darkMode ? .black : .systemBlue
6    setNeedsStatusBarAppearanceUpdate()
7}

Semantic colors are often safer than fixed RGB choices because they adapt better to accessibility and appearance changes.

Status bar appearance can seem inconsistent when screens are presented modally. In full-screen presentation, the presented controller may become responsible for the status bar style. That means each full-screen screen should manage its own preferredStatusBarStyle and top-area background if its design differs from the presenting screen.

This is one reason status bar code that appears correct in a pushed navigation flow may look wrong in a modal flow.

Common Pitfalls

The biggest mistake is using private APIs or old snippets that reach into hidden status bar views. Those approaches are fragile and can break across iOS releases.

Another common issue is forgetting that the visible controller may actually be a navigation controller. If the container does not forward style queries, your override never takes effect.

Hard-coded colors are also risky. A design that looks readable in light mode can fail immediately in dark mode or with higher contrast settings.

Finally, many developers change internal theme state but forget to call setNeedsStatusBarAppearanceUpdate(). Without that call, the bar can remain stuck on the old text color.

Summary

  • Use preferredStatusBarStyle to control status bar text and icon color.
  • Color the area behind the status bar with a normal view overlay.
  • Forward status bar style from navigation controllers when needed.
  • Test light mode, dark mode, push navigation, and modal presentation paths.
  • Avoid private APIs for status bar styling on iOS 13.

Course illustration
Course illustration

All Rights Reserved.