Fix: preferredStatusBarStyle Ignored in iOS View Controllers

Fix: preferredStatusBarStyle Ignored in iOS View Controllers

Written by

in

Quick answer: Ensure UIViewControllerBasedStatusBarAppearance is set to YES (or removed, since true is the default) in your Info.plist. In Swift, override preferredStatusBarStyle as a read-only computed property rather than a function. If your view controller is embedded in a UINavigationController, you must also override childForStatusBarStyle on the navigation controller or configure the navigation bar style directly.

Why this happens

When you override preferredStatusBarStyle in a UIViewController subclass and notice the status bar text color never changes, one of three architectural issues is usually responsible.

First, historical projects often have the UIViewControllerBasedStatusBarAppearance key set to NO (false) in their Info.plist. When this key is set to NO, UIKit ignores all view controller preferences and exclusively honors the legacy global property UIApplication.shared.statusBarStyle. Because modern iOS versions have deprecated direct modification of statusBarStyle on UIApplication, relying on that key causes unexpected behavior.

Second, container view controllers (such as UINavigationController and UITabBarController) intercept status bar queries by default. When a child view controller is embedded inside a navigation controller, UIKit queries the navigation controller itself for preferredStatusBarStyle, completely ignoring the child controller’s override unless explicitly told otherwise.

Third, developers transitioning from earlier Swift syntax often declare preferredStatusBarStyle() as a function rather than a computed property var preferredStatusBarStyle: UIStatusBarStyle. In modern Swift, UIKit exposes this API as a read-only property; defining it as a function creates an unrelated custom method that the runtime never invokes.

How to fix it

1. Enable view controller-based appearance in Info.plist

By default, modern iOS applications use view controller-based status bar appearance. However, if your project was migrated or altered previously, verify your Info.plist settings.

  1. Open your project in Xcode.
  2. Select your application target and navigate to the Info tab, or open Info.plist directly.
  3. Locate the key View controller-based status bar appearance (UIViewControllerBasedStatusBarAppearance).
  4. Set its value to YES (Boolean true), or delete the key entirely so it falls back to the default YES behavior.

If this entry is set to NO, preferredStatusBarStyle will never be called by the system.

2. Override preferredStatusBarStyle as a computed property

Ensure you are overriding the modern Swift computed property on your UIViewController subclass:

import UIKit

class ProfileViewController: UIViewController {

    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .black
    }
}

If the status bar style changes dynamically while the view controller is active (for example, when switching between dark and light sections of an interface), call setNeedsStatusBarAppearanceUpdate() to tell UIKit to refresh the appearance:

var isDarkModeEnabled: Bool = false {
    didSet {
        setNeedsStatusBarAppearanceUpdate()
    }
}

override var preferredStatusBarStyle: UIStatusBarStyle {
    return isDarkModeEnabled ? .lightContent : .darkContent
}

3. Forward status bar queries from UINavigationController

If your view controller is pushed onto or embedded in a UINavigationController, UIKit asks the UINavigationController for its status bar style. By default, standard navigation controllers determine status bar style based on the bar style of the navigation bar, not the top view controller.

To make a navigation controller defer to its visible child controller, create a custom subclass of UINavigationController and override childForStatusBarStyle:

import UIKit

class CustomNavigationController: UINavigationController {
    override var childForStatusBarStyle: UIViewController? {
        return topViewController
    }
}

Alternatively, you can configure your UINavigationBar appearance directly. Setting the navigation bar’s barStyle to .black automatically switches the status bar text to white across the entire stack:

navigationController?.navigationBar.barStyle = .black

4. Forward status bar queries from UITabBarController

Similar to navigation controllers, a UITabBarController will absorb the status bar style request unless instructed to delegate to the selected tab. Create a custom subclass of UITabBarController:

import UIKit

class CustomTabBarController: UITabBarController {
    override var childForStatusBarStyle: UIViewController? {
        return selectedViewController
    }
}

When users switch tabs, UIKit will now query whichever view controller is currently active in selectedViewController.

5. Handle modal presentations

When presenting a view controller modally using styles like .pageSheet or .formSheet, the presenting controller might retain status bar control depending on the presentation context.

If you present a modal fullscreen and want it to dictate the status bar style, set modalPresentationCapturesStatusBarAppearance:

let modalVC = DetailsViewController()
modalVC.modalPresentationStyle = .overFullScreen
modalVC.modalPresentationCapturesStatusBarAppearance = true
present(modalVC, animated: true)

This ensures the presented controller’s preferredStatusBarStyle takes precedence over the underlying view controller.

How to prevent it

  • Do not use UIApplication.shared.statusBarStyle in new code; it is deprecated and conflicts with multi-window scenes and modern UIKit guidelines.
  • Subclass UINavigationController once at the architecture level to consistently return topViewController from childForStatusBarStyle across your entire application.
  • Remember to pair any dynamic variable changes that affect preferredStatusBarStyle with a call to setNeedsStatusBarAppearanceUpdate().
  • Verify container controllers (UITabBarController, UIPageViewController, UISplitViewController) delegate their appearance requests down to active child controllers.

FAQ

Why does my status bar stay dark when pushing onto a navigation controller?

Because UINavigationController manages its own status bar style based on its navigation bar configuration. You must override childForStatusBarStyle on the navigation controller to return topViewController, or update the barStyle of the UINavigationBar.

When should I call setNeedsStatusBarAppearanceUpdate()?

Call setNeedsStatusBarAppearanceUpdate() whenever an internal state change causes preferredStatusBarStyle to return a different value while the view controller remains active on screen. You do not need to call it during standard view lifecycle transitions like viewDidLoad.

Can I still set the status bar style in AppDelegate or SceneDelegate?

Directly mutating UIApplication.shared.statusBarStyle is deprecated. You should configure the style per view controller using preferredStatusBarStyle or configure the root controller’s navigation bar appearance.

Sources & credits

Parts of this guide are adapted from content licensed under CC BY-SA 3.0; this article is shared under the same license.