May 3, 2020

SwiftUI View and status bar style

In UIKit we can override preferredStatusBarStyle to change the status bar style.


class MyViewController: UIViewController {
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
}

Now when you push another SwiftUI View to NavigationView, there is no way to change the status bar style for the screen you are pushing.


var body: some View {
    HStack {
        NavigationLink(destination: AnotherScreenView()) {
            Text("Push another screen")                
        }        
    }
}

A small trick is to always create a new instance of UIHostingController and change statusbar as required.



First create a subclass of UIHostingController that takes statusBarStyle.


class HostingController: UIHostingController<AnyView> {

    var statusBarStyle: UIStatusBarStyle

    init(rootView: AnyView, statusBarStyle : UIStatusBarStyle = .lightContent) {
        self.statusBarStyle = statusBarStyle
        super.init(rootView: rootView)
    }

    override var preferredStatusBarStyle: UIStatusBarStyle {
        statusBarStyle
    }
}

A small router that holds the UINavigationController, and injected to view hierarchy as an environment object of root view. The only method is push taking a root view and statusBarStyle.


class Router: ObservableObject {
    let navigationController: UINavigationController
    
    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }
    
    func push(_ view: AnyView, statusBarStyle : UIStatusBarStyle = .lightContent) {
        let hostingController =  HostingController(rootView: view, statusBarStyle: statusBarStyle:)
        self.navigationController.pushViewController(hostingController, animated: true)
    }
}


Make a small replacement for SwiftUI's NavigationLink.


struct MyNavigationLink<Destination: View, Content: View>: View {
    @EnvironmentObject 
    var router: Router
    let destination: Destination

    let viewBuilder: () -> Content
    var body: some View {
        Button(action: {        
        self.router.push(AnyView(self.destination), statusBarStyle: .lightContent)
        }) {
            viewBuilder()
        }
    }
}

Now usage is simple


var body: some View {
    HStack {
    MyNavigationLink(destination: AnotherScreenView(), statusBarStyle: .darkContent) {
            Text("Push another screen")                
        }        
    }
}

Tagged with: