Status bar color in iOS 16

Hello! Thanks for taking your time to help others.

Problem description:

Our SwiftUI app min deployment is iOS 14. We've been struggling with status bar* color just in iOS 16.

We can't change its color/appearance from light to dark.

(*) For instance, this picture refers to what I think is status bar (may be obvious, yes).

Previous valid solution (iOS 13 to iOS 15)

Since now we used to manage the appearance with ...

  1. Call in specific view, when a condition is given, like no dark mode supported, or side menu have been open

UIApplication.setStatusBarStyle(.darkContent)

  1. The extension to support this on any SwiftUI View
import UIKit

extension UIApplication {
    class func setStatusBarStyle(_ style: UIStatusBarStyle) {
        if let controller = UIApplication.getKeyWindow()?.rootViewController as? ContentHostingController {
            controller.changeStatusBarStyle(style)
        }
    }
}
  1. Actually, the controller class
import SwiftUI
import UIKit

class ContentHostingController: UIHostingController<AnyView> {

  private var currentStatusBarStyle: UIStatusBarStyle = .default

  override var preferredStatusBarStyle: UIStatusBarStyle {
    currentStatusBarStyle
  }

  func changeStatusBarStyle(_ style: UIStatusBarStyle) {
    self.currentStatusBarStyle = style
    self.setNeedsStatusBarAppearanceUpdate()
  }
}

But, as I said, this stopped working with iOS 16.

What we have checked?

As we googled it...

  1. Using .preferredColorScheme modifier, changes the app appearance, and we want to keep, for example, the dark theme along light status bar. So it's not an option.
  2. Applying .toolbarColorScheme (iOS 16), does nothing.
  3. Manage Info.plist without success. Light content by default, could be changed (with the method described above)

Questions

  1. Why is this not working in iOS 16? Are we doing something wrong? (worked like a charm for 13 to 15).

  2. Is there any generic (iOS 13 to 16) way that you actually use and work?

  3. (If not) Do you know any workaround to apply on 16 only?

Answered by msanchez_iz in 742670022

Final solution (by @ashley-mills and refined by me):

You can also check at StackOverflow

First, a View extension to keep it clean:

func iOS16navBarAdapter(_ colorScheme: ColorScheme) -> some View {
        if #available(iOS 16, *) {
            return self
                .toolbarBackground(Color.navigationBar, for: .navigationBar)
                .toolbarBackground(.visible, for: .navigationBar)
                .toolbarColorScheme(colorScheme, for: .navigationBar)
        } else {
            return self
        }
    }

You can use as follows on your view:

@State private var iOS16colorScheme: ColorScheme = .dark

...

VStack {
    ...
}
.iOS16navBarAdapter(iOS16colorScheme)
.onChange(of: globalSideMenu.xCoord) { value in
    iOS16colorScheme = value > sideMenuOffset ? .light : .dark
}
Accepted Answer

Final solution (by @ashley-mills and refined by me):

You can also check at StackOverflow

First, a View extension to keep it clean:

func iOS16navBarAdapter(_ colorScheme: ColorScheme) -> some View {
        if #available(iOS 16, *) {
            return self
                .toolbarBackground(Color.navigationBar, for: .navigationBar)
                .toolbarBackground(.visible, for: .navigationBar)
                .toolbarColorScheme(colorScheme, for: .navigationBar)
        } else {
            return self
        }
    }

You can use as follows on your view:

@State private var iOS16colorScheme: ColorScheme = .dark

...

VStack {
    ...
}
.iOS16navBarAdapter(iOS16colorScheme)
.onChange(of: globalSideMenu.xCoord) { value in
    iOS16colorScheme = value > sideMenuOffset ? .light : .dark
}

Thank you for the solution @msanchez_iz

This seems to work OK until I start trying to customize the titlebar. If I start setting navigationTitle or customize the toolbar in other ways (e.g. with .toolbar) it starts ignoring the .toolbarColorScheme Not sure if I'm using the title and toolbar settings incorrectly for this, though. Are you changing the toolbar title or content in your actual app, and is the status bar still respecting .toolbarColorScheme?

E.g., if I modify the example on StackOverflow slightly to the code below (without the extension you've posted in the final solution), the .toolbarColorScheme stops working as expected for me. (E.g. without Status bar style set and in light mode, the below example won't change from a dark status bar. If I remove the .navigationTitle it will set the status bar to light as expected.) Again, not sure if I'm missing something or doing something incorrectly. I'm using Xcode 14.2/iOS 16.2 to try it out.

struct ContentView: View {
    var body: some View {
        NavigationStack {
            TestView(title: "Top", colorScheme: .light)
        }
    }
}

struct TestView: View {
    let title: String
    let colorScheme: ColorScheme
    
    var body: some View {
        ZStack {
            Color.mint
                .ignoresSafeArea()
            VStack {
                Text("Hello")
                NavigationLink("Push") {
                    TestView(title: "Pushed", colorScheme: .light)
                }
            }
        }
        .navigationTitle(title)
        .toolbarBackground(.mint, for: .navigationBar)
        .toolbarBackground(.visible, for: .navigationBar)
        .toolbarColorScheme(colorScheme, for: .navigationBar)
    }
}
Status bar color in iOS 16
 
 
Q