Moacir Braga
Moacir Braga

Reputation: 174

How to change StatusBarStyle on SwiftUI App Lifecycle?

I already spent a lot of hours trying to figure out a way to change statusBarStyle to light/dark using the new lifecycle SwiftUI App.

The newest posts about the status bar teach how to hide it, but I don't want to do it, I just need to change it to dark or light.

To change the color, the most recent way I found is open SceneDelegate.swift and change window.rootViewController to use my own HostingController, but it will only work for projects using UIKit App Delegate Lifecycle. Using SwiftUI App Lifecycle, the SceneDelegate.swift will not be generated, so where can I do it?

I can do it via General Settings on the Xcode interface. My question is about how to do it via code dynamically.

Below is what I got so far.

Everything.swift

import Foundation
import SwiftUI

class LocalStatusBarStyle { // style proxy to be stored in Environment
    fileprivate var getter: () -> UIStatusBarStyle = { .default }
    fileprivate var setter: (UIStatusBarStyle) -> Void = {_ in}

    var currentStyle: UIStatusBarStyle {
        get { self.getter() }
        set { self.setter(newValue) }
    }
}

struct LocalStatusBarStyleKey: EnvironmentKey {
    static let defaultValue: LocalStatusBarStyle = LocalStatusBarStyle()
}

extension EnvironmentValues { // Environment key path variable
    var localStatusBarStyle: LocalStatusBarStyle {
        get {
            return self[LocalStatusBarStyleKey.self]
        }
    }
}

class MyHostingController<Content>: UIHostingController<Content> where Content:View {
    private var internalStyle = UIStatusBarStyle.default

    @objc override dynamic open var preferredStatusBarStyle: UIStatusBarStyle {
        get {
            internalStyle
        }
        set {
            internalStyle = newValue
            self.setNeedsStatusBarAppearanceUpdate()
        }
    }
    
    override init(rootView: Content) {
        super.init(rootView:rootView)

        LocalStatusBarStyleKey.defaultValue.getter = { self.preferredStatusBarStyle }
        LocalStatusBarStyleKey.defaultValue.setter = { self.preferredStatusBarStyle = $0 }
    }

    @objc required dynamic init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

struct TitlePage: View {
    @Environment(\.localStatusBarStyle) var statusBarStyle
    @State var title: String

    var body: some View {
        Text(title).onTapGesture {
            if self.statusBarStyle.currentStyle == .darkContent {
                self.statusBarStyle.currentStyle = .default
                self.title = "isDefault"
            } else {
                self.statusBarStyle.currentStyle = .darkContent
                self.title = "isDark"
            }
        }
    }
}

struct ContainerView: View {
    var controllers: [MyHostingController<TitlePage>]
    
    init(_ titles: [String]) {
        self.controllers = titles.map { MyHostingController(rootView: TitlePage(title: $0)) }
    }
    
    var body: some View {
        PageViewController(controllers: self.controllers)
    }
}

struct PageViewController: UIViewControllerRepresentable {
    var controllers: [UIViewController]
    
    func makeUIViewController(context: Context) -> UIPageViewController {
        let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
        return pageViewController
    }
    
    func updateUIViewController(_ uiViewController: UIPageViewController, context: Context) {
        uiViewController.setViewControllers([controllers[0]], direction: .forward, animated: true)
    }
    
    typealias UIViewControllerType = UIPageViewController
}

MyApp.swift

import SwiftUI

@main
struct TestAppApp: App {
    var body: some Scene {
        WindowGroup {
            ContainerView(["Subscribe", "Comment"])
        }
    }
}

struct TestAppApp_Previews: PreviewProvider {
    static var previews: some View {
        Text("Hello, World!")
    }
}

enter image description here

Upvotes: 8

Views: 3414

Answers (4)

Burgler-dev
Burgler-dev

Reputation: 317

Add two values to the Info.plist:

<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>

This works with the new SwiftUI App Lifecycle (@main). Verified on iOS14.4.

enter image description here

Upvotes: 6

This is not really a solution but the best I could come up with (and ended up doing) was to force app to the dark mode. Either in Info.plist or NavigationView { ... }.preferredColorScheme(.dark)

That will also change the statusBar. You will not be able to change the status bar style per View though.

Upvotes: -1

Ajaz Ahmed
Ajaz Ahmed

Reputation: 11

My suggestion is you just create an AppDelegate Adaptor and do whatever customization you need from there. SwiftUI will handle the creation of AppDelegate and managing its lifetime.

Create an AppDelegate class:

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        UIApplication.shared.statusBarStyle = .darkContent
        return true
    }
}

Now inside your App:

@main
struct myNewApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            Text("I am a New View")
        }
    }
} 

Upvotes: 1

Christian Ray Leovido
Christian Ray Leovido

Reputation: 758

Before, with SceneDelegate

(code taken from SwiftUI: Set Status Bar Color For a Specific View)

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        ...
        window.rootViewController = MyHostingController(rootView: contentView)
}

After, with no SceneDelegate

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            MyHostingController(rootView: ContentView())
        }
    }
}

If you follow the answer from the link above and apply it here with the @main, you should be able to achieve your changes.

Upvotes: -3

Related Questions