Swift
UIStatusBarStyle
iOS development
SwiftUI
debugging

UIStatusBarStyle not working in Swift

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

UIStatusBarStyle controls whether the status bar text and icons appear light or dark. The most common reason it stops working is that UIViewControllerBasedStatusBarAppearance is set to NO in Info.plist while trying to set the style per view controller, or the view controller hierarchy (especially UINavigationController) overrides individual controller preferences. In iOS 13+ with dark mode, the .default style adapts automatically, and UINavigationBarAppearance takes over status bar styling in navigation-based apps. Fixing this requires understanding which object controls the status bar in your specific view controller hierarchy.

Setting Status Bar Style Per View Controller

swift
1class MyViewController: UIViewController {
2    override var preferredStatusBarStyle: UIStatusBarStyle {
3        return .lightContent  // White text for dark backgrounds
4    }
5
6    override func viewDidAppear(_ animated: Bool) {
7        super.viewDidAppear(animated)
8        setNeedsStatusBarAppearanceUpdate()
9    }
10}

preferredStatusBarStyle is a computed property that the system reads when it needs the status bar style. Call setNeedsStatusBarAppearanceUpdate() to tell the system to re-query this property after a change.

The Info.plist Setting

xml
1<!-- Info.plist -->
2<!-- This MUST be YES (or absent) for per-controller styles to work -->
3<key>UIViewControllerBasedStatusBarAppearance</key>
4<true/>

If UIViewControllerBasedStatusBarAppearance is NO, the system ignores preferredStatusBarStyle on all view controllers. The default value is YES (since iOS 7), so if you never set it, per-controller styling works. But some third-party libraries or templates set it to NO, which breaks per-controller control.

UINavigationController Overrides

swift
1// UINavigationController does NOT forward preferredStatusBarStyle
2// to its child view controllers by default.
3// The NAVIGATION CONTROLLER's style wins.
4
5// Fix 1: Subclass UINavigationController
6class StatusBarNavigationController: UINavigationController {
7    override var preferredStatusBarStyle: UIStatusBarStyle {
8        return topViewController?.preferredStatusBarStyle ?? .default
9    }
10
11    override var childForStatusBarStyle: UIViewController? {
12        return topViewController
13    }
14}
swift
1// Fix 2: Use childForStatusBarStyle in a container
2class ContainerViewController: UIViewController {
3    override var childForStatusBarStyle: UIViewController? {
4        return children.first  // Delegate to the embedded child
5    }
6}

UINavigationController, UITabBarController, and UISplitViewController determine the status bar style for their children. Override childForStatusBarStyle to delegate the decision to a specific child view controller.

UINavigationBarAppearance (iOS 13+)

swift
1// iOS 13+ — NavigationBar appearance controls status bar in navigation stacks
2let appearance = UINavigationBarAppearance()
3appearance.configureWithOpaqueBackground()
4appearance.backgroundColor = .systemBlue
5
6// This sets light status bar content for this navigation bar
7appearance.backgroundEffect = nil
8
9let navController = UINavigationController(rootViewController: myVC)
10navController.navigationBar.standardAppearance = appearance
11navController.navigationBar.scrollEdgeAppearance = appearance
12
13// For dark nav bar → light status bar
14navController.navigationBar.barStyle = .black  // Forces light status bar text

Setting barStyle = .black on a navigation bar forces the status bar to use light content, regardless of preferredStatusBarStyle.

SwiftUI Status Bar Style

swift
1// SwiftUI — use .preferredColorScheme or toolbarColorScheme
2struct ContentView: View {
3    var body: some View {
4        NavigationStack {
5            VStack {
6                Text("Hello")
7            }
8            .frame(maxWidth: .infinity, maxHeight: .infinity)
9            .background(.black)
10            .toolbarColorScheme(.dark, for: .navigationBar)  // iOS 16+
11        }
12    }
13}
14
15// Alternative: hide and show with custom styling
16struct DarkView: View {
17    var body: some View {
18        ZStack {
19            Color.black.ignoresSafeArea()
20            Text("Dark Background")
21                .foregroundStyle(.white)
22        }
23        .preferredColorScheme(.dark)  // Forces light status bar
24    }
25}

In SwiftUI, .preferredColorScheme(.dark) sets the entire view to dark mode appearance, which includes a light status bar. Use .toolbarColorScheme (iOS 16+) for more granular control in navigation stacks.

Debugging Status Bar Style

swift
1// Check which controller controls the status bar
2override func viewDidAppear(_ animated: Bool) {
3    super.viewDidAppear(animated)
4
5    // Walk up the controller hierarchy
6    var controller: UIViewController? = self
7    while let current = controller {
8        print("Controller: \(type(of: current))")
9        print("  preferredStatusBarStyle: \(current.preferredStatusBarStyle.rawValue)")
10        print("  childForStatusBarStyle: \(String(describing: current.childForStatusBarStyle))")
11        controller = current.parent
12    }
13}

Walk the view controller hierarchy to find which controller is actually determining the status bar style. The topmost container controller wins unless it delegates via childForStatusBarStyle.

swift
1// Modal view controllers control their own status bar
2let modal = ModalViewController()
3modal.modalPresentationStyle = .fullScreen
4
5// For .fullScreen — modal controls status bar
6// For .pageSheet / .formSheet (iOS 13+) — presenting controller still controls it
7
8// Override for non-fullscreen modals
9modal.modalPresentationCapturesStatusBarAppearance = true
10present(modal, animated: true)

Only .fullScreen modal presentations automatically transfer status bar control to the presented controller. For other styles, set modalPresentationCapturesStatusBarAppearance = true.

Common Pitfalls

  • UIViewControllerBasedStatusBarAppearance set to NO: This disables per-view-controller styling entirely. Remove this key from Info.plist or set it to YES. The legacy UIApplication.shared.statusBarStyle setter was deprecated in iOS 9.
  • UINavigationController swallowing the style: Navigation controllers do not forward preferredStatusBarStyle to their children by default. Subclass UINavigationController and override childForStatusBarStyle to return topViewController.
  • Forgetting setNeedsStatusBarAppearanceUpdate(): Changing a property that affects preferredStatusBarStyle does not automatically update the status bar. Call setNeedsStatusBarAppearanceUpdate() after the change to trigger a refresh.
  • barStyle conflicting with preferredStatusBarStyle: Setting navigationBar.barStyle = .black forces light status bar content regardless of the view controller's preferredStatusBarStyle. Remove the barStyle override or set it to .default to allow per-controller styling.
  • Dark mode automatic adaptation: In iOS 13+, .default adapts to the current user interface style (dark text in light mode, light text in dark mode). If you hardcode .lightContent and the user switches to light mode, the status bar may become invisible against a white background.

Summary

  • Override preferredStatusBarStyle in your view controller and call setNeedsStatusBarAppearanceUpdate() to trigger it
  • Ensure UIViewControllerBasedStatusBarAppearance is YES (or absent) in Info.plist
  • Subclass UINavigationController to forward childForStatusBarStyle to topViewController
  • Use navigationBar.barStyle = .black to force light status bar content in navigation stacks
  • In SwiftUI, use .preferredColorScheme(.dark) or .toolbarColorScheme for status bar control
  • For modals that are not full screen, set modalPresentationCapturesStatusBarAppearance = true

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.