Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

  • Target: iOS 14
  • IDE: Xcode 12 beta 3
  • OS: MacOS 11 Big Sur

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

like image 786
Moacir Braga Avatar asked Jul 25 '20 20:07

Moacir Braga


1 Answers

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

like image 169
Burgler-dev Avatar answered Nov 15 '22 05:11

Burgler-dev