2

Setup:

Swift 5.5, Xcode 13.2

This topic is all over the web and here's one of them. Well, my app has three options:

  • On: Use dark mode no matter what the system is set to
  • Off: Use light mode no matter what the system is set to
  • System: Use the system defaults (Light or Dark)

I have a simple class that toggles the three:

import SwiftUI

class Utilities: ObservableObject {

    // The default is to use the system's default.
    @AppStorage("theme") var theme: String = ""
    
    var userInterfaceStyle: ColorScheme? = .dark

    func overrideDisplayMode() {
        var userInterfaceStyle: UIUserInterfaceStyle

        if theme == "On" {
            userInterfaceStyle = .dark
        } else if theme == "Off" {
            userInterfaceStyle = .light
        } else {
            // System
            userInterfaceStyle = .unspecified
        }
        
        let scenes = UIApplication.shared.connectedScenes
        let windowScene = scenes.first as? UIWindowScene
        let window = windowScene?.windows.first
        
        window?.overrideUserInterfaceStyle = userInterfaceStyle
        
    }
}

How it's updated (minus the button function etc):


@main
struct MainApp: App {
   @StateObject var utilities = Utilities()

  var body: some Scene {
    WindowGroup {
       ContentView()
       .onChange(of: utilities.theme, perform: { _ in
         utilities.overrideDisplayMode()
       })
       
       // Ensures the theme is set when app first loads
       .onAppear(perform: {
          utilities.overrideDisplayMode()
       }
    }
  }
}

This works well: theming. The only issue is the status bar. I'd like to change the status bar colour whenever I overrideUserInterfaceStyle or whenever theme is changed. How can I achieve this with this setup?

Sylar
  • 11,422
  • 25
  • 93
  • 166

1 Answers1

0

It was simple enough. Inside Apple's UIApplication.h I see

public enum UIStatusBarStyle : Int {

    
    case `default` = 0 // Automatically chooses light or dark content based on the user interface style

    @available(iOS 7.0, *)
    case lightContent = 1 // Light content, for use on dark backgrounds

    @available(iOS 13.0, *)
    case darkContent = 3 // Dark content, for use on light backgrounds
}

For me, the key part for me was:

case default = 0 // Automatically chooses light or dark content based on the user interface style

So proceeded:


[..]
func overrideDisplayMode() {
   [..]
        
    window?.overrideUserInterfaceStyle = userInterfaceStyle
    UIApplication.shared.statusBarStyle = .default // <-- THIS
        
 }

"THIS" did not work so I made a full change:

[..]

func overrideDisplayMode() {
    var userInterfaceStyle: UIUserInterfaceStyle
    // New
    var barStyle: UIStatusBarStyle

    if theme == "On" {
        userInterfaceStyle = .dark
        // New
        barStyle = .lightContent
    } else if theme == "Off" {
        userInterfaceStyle = .light
        // New
        barStyle = .darkContent
    } else {
        userInterfaceStyle = .unspecified
        // New
        barStyle = .default
    }
    
    let scenes = UIApplication.shared.connectedScenes
    let windowScene = scenes.first as? UIWindowScene
    let window = windowScene?.windows.first
    
    window?.overrideUserInterfaceStyle = userInterfaceStyle
    // New
    UIApplication.shared.statusBarStyle = barStyle
    
}

Then inside info.plist:

View controller-based status bar appearance: NO

While this change works, I get a deprecation warning for iOS 13:

'statusBarStyle' was deprecated in iOS 13.0: Use the statusBarManager property of the window scene instead.

I cannot figure out how to use statusBarManager but for now, I'll figure out that part later.

Sylar
  • 11,422
  • 25
  • 93
  • 166