MglSaRu
MglSaRu

Reputation: 173

Status bar color in iOS 16

Thanks for taking your time to help others.

This is the original post, I publish here also hoping for better luck. Thanks!

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.

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)

2- 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)
        }
    }
}

3- 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?

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

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
}

Upvotes: 7

Views: 4035

Answers (1)

Ashley Mills
Ashley Mills

Reputation: 53111

There are a couple of ways to change the status bar colour.

Starting with this View:

struct ContentView: View {

    var body: some View {
        ZStack {
            Color.mint
                .ignoresSafeArea()
            Text("Hello")
        }
    }
}

and setting the following info.plist keys:

  • View controller-based status bar appearance: NO

  • Status bar style: Dark Content

gives:

enter image description here

Changing

  • Status bar style: Light Content

gives:

enter image description here

You can also change the status bar text colour on a per view basis, making sure you set

  • View controller-based status bar appearance: YES

then wrapping your view in a NavigationStack, and adding the following toolbar modifiers:


.toolbarBackground(.mint, for: .navigationBar)
.toolbarBackground(.visible, for: .navigationBar)
.toolbarColorScheme(<colorScheme>, for: .navigationBar)

for example:

struct ContentView: View {
    
    var body: some View {
        NavigationStack {
            TestView(colorScheme: .dark)
        }
    }
}

struct TestView: View {
    
    let colorScheme: ColorScheme
    
    var body: some View {
        ZStack {
            Color.mint
                .ignoresSafeArea()
            VStack {
                Text("Hello")
                NavigationLink("Push") {
                    TestView(colorScheme: .dark)
                }
            }
        }
        .toolbarBackground(.mint, for: .navigationBar)
        .toolbarBackground(.visible, for: .navigationBar)
        .toolbarColorScheme(colorScheme, for: .navigationBar)
    }
}

gives:

enter image description here

Upvotes: 5

Related Questions