6

I have been attempting to set the statusbar in my SwiftUI app to light text as it has a dark background.

I found this solution on several sites but cannot get it to work.

HostingController.swift

import Foundation
import UIKit
import SwiftUI

class HostingController : UIHostingController {
    override var preferredStatusBarStyle: UIStatusBarStyle {
    return .lightContent
    }
}

This returns an error on the class declaration line Reference to generic type 'UIHostingController' requires arguments in <...> with a suggested fix of Insert '<<#Content: View#>>'. Applying said fix results in the error Use of undeclared type '<#Content: View#>'

You are then meant to change the window.rootViewController in the SceneDelegate.swift file.

SceneDelegate.swift

...

// Create the SwiftUI view that provides the window contents.
        let contentView = Login()

        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = HostingController(rootView: contentView)
            self.window = window
            window.makeKeyAndVisible()
        }

...

This throws an error on the window.rootViewController line Argument passed to call that takes no arguments

Anyone got any ideas? Seems like a lot of bother just to set the status bar colour which I imagine would be a fairly common requirement.

iShaymus
  • 512
  • 7
  • 26

4 Answers4

6

Your HostingController needs a concrete type of the rootView:

class HostingViewController<Content: View>: UIHostingController<Content> {

    @objc override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
}

Then use it in func scene(_ scene: UIScene, willConnectTo... as the rootViewController:

let contentView = ContentView()
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        
        window.rootViewController = HostingViewController(rootView: AnyView(contentView.environmentObject(SessionStore())))
        self.window = window
        window.makeKeyAndVisible()
    }

You won't see any difference in canvas unfortunately, but try it on a simulator.

Daniel Saidi
  • 6,079
  • 4
  • 27
  • 29
LuLuGaGa
  • 13,089
  • 6
  • 49
  • 57
  • Thanks. This works. However I have to remove my `environmentObject` to make it work `HostingController(rootView: contentView.environmentObject(SessionStore())` otherwise it requires fixing to `HostingController(rootView: contentView.environmentObject(SessionStore()) as! ContentView)` which then throws a `SIGABRT` error when run. – iShaymus Sep 29 '19 at 11:01
  • Try declaring like this: HostingViewController: UIHostingController and then erasing your rootview to AnyView HostingController(rootView: AnyView(contentView.environmentObject(SessionStore()))) – LuLuGaGa Sep 29 '19 at 11:33
  • `window.rootViewController = AnyView HostingController(rootView: AnyView(contentView.environmentObject(SessionStore())))` throws the error `Cannot assign value of type 'AnyView.Type' to type 'UIViewController?'` – iShaymus Sep 29 '19 at 19:50
  • It's not quite what I had in mind. Please see my edited answer. – LuLuGaGa Sep 29 '19 at 20:02
  • Works like a charm `window.rootViewController = HostingController(rootView: AnyView(contentView.environmentObject(SessionStore())))` – iShaymus Sep 29 '19 at 20:50
  • I'm a bit late, but you can use generics better: – Roland.R Aug 04 '20 at 16:50
6

Another way to extend UIHostingController is to keep the generic Content type, then you don't have to pass the session store:

class HostingController<Content>: UIHostingController<Content> where Content: View {
    @objc override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
}

then in your scene delegate:

window.rootViewController = HostingController(rootView: contentView)
ldiqual
  • 15,015
  • 6
  • 52
  • 90
  • This works awesome. My issue was the some: View and onTapGesture wouldn't work unless the generics were correct as above. I also didn't need the @objc. Which let me also do: ``` if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) window.rootViewController = HostingController( rootView: homeView.onTapGesture { window.endEditing(true) } ) self.window = window window.makeKeyAndVisible() } ``` – Madhava Jay Mar 05 '20 at 02:14
3

In iOS 14 you just need to change/add 2 keys in Info.plist:

enter image description here

Rufat Mirza
  • 1,425
  • 14
  • 20
  • 1
    This is the correct answer as of iOS 14. There is no longer any requirement to code this in. Change the settings as indicated above in the `Info.plist` file. – iShaymus Feb 12 '21 at 07:57
  • 1
    Here's a video showing how to update the Info.plist: https://youtu.be/w-I5I8POMSI – Richard H Apr 02 '21 at 03:19
0

I'm a bit late, but you can use generics better:

class HostingController<ContentView: View>: UIHostingController<ContentView> {
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .darkContent
    }
}

This allows you to pass any view to the HostingController even with .environmentObject() view modifier.

Roland.R
  • 176
  • 1
  • 3