Why Is Manually Setup Root View Controller Showing Black Screen

Why is manually setup root view controller showing black screen?

To ensure you see your root view controller in iOS 13 when everything is done programmatically, you must do the following:

In the scene delegate, you must create the window instance and the root view controller:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let winScene = (scene as? UIWindowScene) else { return }

// Create the root view controller as needed
let vc = ViewController()
let nc = UINavigationController(rootViewController: vc)

// Create the window. Be sure to use this initializer and not the frame one.
let win = UIWindow(windowScene: winScene)
win.rootViewController = nc
win.makeKeyAndVisible()
window = win
}
}

Your Info.plist has to have the "Application Scene Manifest" entry. Below it should be the "Enable Multiple Windows" entry. Set to YES or NO as appropriate to your app. Optionally you should also have the "Scene Configuration" entry.

All of these entries are added by Xcode when you check the "Supports multiple windows" setting on the General tab of your target. This will default the "Enable Multiple Windows" entry to YES so you can change that to NO if you want scenes but not multiple windows.

iOS - Setting 'rootViewController' results in black screen on device only

It looks like the ViewController is not set to display anything. Unless you are using a xib (in which case you would need to load the view controller in a different way, see below), there is nothing describing how the ViewController's view should render.

To test this out, you can add the line self.view.backgroundColor = UIColor.red to the ViewController's viewDidLoad() method, then run it again on the device- if the background color turns red, then hooray! The next step will be programmatically adding a UILabel.


Loading UIViewController From a XIB

let vc = MyViewController(nibName: "xibname", bundle: nil)

Alternatively, you can mask the loading by adding a custom init inside MyViewController:

class MyViewController: UIViewController {
required init() {
super.init(nibName: "xibname", bundle: nil)
}
}

(Thank you zonily-jame for the addition of hiding it in the class)

Initial ViewController with Firebase bug fix

I think there are 2 ways to solve this issue.

1. Using a splash screen:

Don't make the decision of showing a login screen or main screen in scene(_:, session, connectionOptions). Instead, create a new view controller called SplashScreenViewController. Set your app's logo in its center. And then in its viewDidLoad() move your logic to check if the user is already logged in:

func chooseAndPresentStartScreen() {
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
if let user = Auth.auth().currentUser {
FirestoreService.shared.getUserData(user: user) { (result) in
switch result {
case .success(let muser):
let navigationController = storyBoard.instantiateViewController(withIdentifier: "Navigation") as! UINavigationController
let conroller = storyBoard.instantiateViewController(withIdentifier: "MainController") as! MainController
navigationController.viewControllers = [conroller]
self.present(navigationController, animated: true, completion: nil)

case .failure(_):
let conroller = storyBoard.instantiateViewController(withIdentifier: "SignUpController") as! SignUpController
self.present(conroller, animated: true, completion: nil)
}
}
} else {
print(3)
let conroller = storyBoard.instantiateViewController(withIdentifier: "SignUpController") as! SignUpController
self.present(conroller, animated: true, completion: nil)
}
}

2. Using User Defaults:

Define a global variable for the key used to save user default value.

let isUserLoggedInKey: String = "IsUserLoggedIn"

After the user logs-in:

let defaults = UserDefaults.standard
defaults.setValue(true, forKey: isUserLoggedInKey)

Then in your scene(_:, session, connectionOptions), check if this value is set.

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
let defaults = UserDefaults.standard
let isLoggedIn = defaults.bool(forKey: isUserLoggedInKey)
if isLoggedIn {

let navigationController = self.storyBoard.instantiateViewController(withIdentifier: "Navigation") as! UINavigationController
let conroller = self.storyBoard.instantiateViewController(withIdentifier: "MainController") as! MainController
navigationController.viewControllers = [conroller]
self.window?.rootViewController = navigationController

} else {
let conroller = self.storyBoard.instantiateViewController(withIdentifier: "SignUpController") as! SignUpController
self.window?.rootViewController = conroller
}
window?.makeKeyAndVisible()

}

Don't forget to set isLoggedInKey to false when the user logs out.

let defaults = UserDefaults.standard
defaults.setValue(false, forKey: isUserLoggedInKey)

If you don't want to manually save value for isUserLoggedInKey when the user logs in and logs out, you can also use a state change listener. It takes a callback that is triggered when the user logs in or logs out. Add this in your Login view controller:

Auth.auth().addStateDidChangeListener { (auth, user) in 
let defaults = UserDefaults.standard
defaults.setValue(user != nil, forKey: isUserLoggedInKey)
}

Changed rootViewController but the view of old rootViewController is still in the View Hierarchy

The reason:

I have this problem when I try to replace rootViewController while logged in using the Google Sign-In SDK and Facebook Login SDK.

These SDKs have an authentication screen like this one:

Sample Image

By using the Debug View Hierarchy, I realized when the authentication screen was presented (2), the app changed the rootViewController to a UITransitionView. And when the authentication screen is dismissed (3), it changed the rootViewController once more to the state before presenting the authentication screen (1).

State (1): Before presenting the authentication screen, rootViewController is LoginViewController.

State (2): Presenting the authentication screen, rootViewController changed to UITransitionView.

State (3): After dismissed the authentication screen, rootViewController has returned to LoginViewController.

States (1) + (3) in Debug View Hierarchy:
Sample Image

State (2) in Debug View Hierarchy:
Sample Image

I put my code to change the rootViewController in the each delegate method of corresponding SDKs that is called when authentication is completed.

Google Sign-In SDK: signIn:didSignInForUser:withError:

Facebook Login SDK: logInWithReadPermissions:fromViewController:handler:

These methods are called immediately after the user has authenticated without regard to the authentication screen is dismissed or not.

It means, sometimes, the problem occurs when the user's authentication process is completed too quickly, even before the authentication screen is dismiss and the rootViewController changes to LoginViewController. It means, the problem is in between two states (2) and (3), when the user has authenticated but rootViewController is still UITransitionView.

The solution:

Temporary, before I can find a better solution, I prevent the user's authentication process from happening too quickly, it means that I wait for state (3) to finish by delaying 0.25 seconds after the user had authenticated and then changing rootViewController.

0.25 is enough time for everything to work and too fast for the user to lose patience.

UINavigationController shows black screen after pushing a view

The accepted answer didn't work for me. I was already using instantiateViewControllerWithIdentifier to navigate to view controller in different storyboard. I am using xcode7.2 and Swift.

I am navigating from storyboard1, some view controller's button action.
Destination is to initial Navigation controller of Storyboard2.

Still I get black screen.

The problem was storyboard2's Navigation controller linked to 1st view controller was linked via show.

Solution:

Delete the link between Nav controller and 1st view controller. Now link it using root view controller. (Ctrl+Click on Navigation controller and drag it to the View controller and Select the option "root view controller")



Related Topics



Leave a reply



Submit