Changing Root View Controller of a iOS Window

Change root View Controller

For 1) you can't present a view controller using a segue and then use it to replace the root view controller in the prepare. You will need to instantiate the tab view controller from the storyboard and then replace the root view controller.

Something like this:

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "TabController")
UIApplication.shared.keyWindow?.rootViewController = vc

(assuming the storyboard is called 'Main' and you give the tab controller the storyboard ID of 'TabController'.

I'm not quite clear on what the issue is for 2.

However as a general note I would approach this differently and instead of having the login controller as your initial view controller have the tab bar as the initial controller and then just present the login controller the first time the app starts. That way you avoid replacing the root controller at all and it's all more controlled.

Programmatically change rootViewController of storyBoard

Objective-C:

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
UITabBarController *rootViewController = [storyboard instantiateViewControllerWithIdentifier:@"tabBarcontroller"];
[[UIApplication sharedApplication].keyWindow setRootViewController:rootViewController];

Swift :

 let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = mainStoryboard.instantiateViewControllerWithIdentifier("tabBarcontroller") as UITabBarController
UIApplication.sharedApplication().keyWindow?.rootViewController = viewController;

Swift 3:

let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = mainStoryboard.instantiateViewController(withIdentifier: "tabBarcontroller") as! UITabBarController
UIApplication.shared.keyWindow?.rootViewController = viewController

Swift 5:

let viewController = mainStoryboard.instantiateViewController(withIdentifier: "tabBarcontroller") as! UITabBarController
UIApplication.shared.windows.first?.rootViewController = viewController
UIApplication.shared.windows.first?.makeKeyAndVisible()

Or simply like this:

let viewController = mainStoryboard.instantiateViewController(withIdentifier: "tabBarcontroller") as! UITabBarController
self.view.window?.rootViewController = viewController
self.view.window?.makeKeyAndVisible()

Both works fine!

Change root ViewController programmatically

Set this in appDelegate's didFinishLaunchingWithOptions method according to current app settings , also you should use window not keyWindow

if(userExists)
{
let vc = storyboard.instantiateViewController(withIdentifier: "tabBarVC")

UIApplication.shared.window.first?.rootViewController = vc

}
else
{
let vc = storyboard.instantiateViewController(withIdentifier: "loginVC")

UIApplication.shared.window.first?.rootViewController = vc
}

Don't use present as this will automatically change the root

Also another way to access windows (used when AppDelegate has a value)

let appDelegate = UIApplication.shared.delegate as? AppDelegate
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let homeController = mainStoryboard.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
appDelegate?.window?.rootViewController = homeController

How to change rootViewController in swift?

First you need to get the Window from SceneDelegate and use that window for rootViewController.

Suggestion: Whenever we are using rootViewController then no need to do present and push thing it will automatically set as a main home viewcontroller(Intial viewController)

 let window = (UIApplication.shared.connectedScenes.first!.delegate as! SceneDelegate).window
let storyboard = UIStoryboard(name: "Main", bundle:nil)
if UserDefaults.standard.bool(forKey: "LoggedIn") == false {
let vc =storyboard.instantiateViewController(withIdentifier: "EnterPin") as! UINavigationController
window?.rootViewController = vc
window?.makeKeyAndVisible()
} else if UserDefaults.standard.bool(forKey: "LoggedIn") == true {
let vc =storyboard.instantiateViewController(withIdentifier: "MainController") as! UINavigationController
window?.rootViewController = vc
window?.makeKeyAndVisible()
}

I suggest you do all this things in SceneDelegate file

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let scene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: scene)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if UserDefaults.standard.bool(forKey: "LoggedIn") == false {
let vc = storyboard.instantiateViewController(withIdentifier: "EnterPin") as! UINavigationController
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()
} else if UserDefaults.standard.bool(forKey: "LoggedIn") == true {
let vc = storyboard.instantiateViewController(withIdentifier: "MainController") as! UINavigationController
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()
}
}

I Hope it is helpful for you!

IOS | Unable to change root view controller in app delegate

If you are targeting only iOS 13+, the only change you should need to make is to add one line:

    window?.rootViewController = initialViewController

// add this line
self.window = window

window?.makeKeyAndVisible()

If you want to support earlier iOS versions, here is a complete SceneDelegate / AppDelegate implementation:

SceneDelegate.swift

//
// SceneDelegate.swift
// Created by Don Mag on 3/27/20.
//

import UIKit

// entire class is iOS 13+
@available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

print("Scene Delegate willConnectTo", UserDefaults.standard.bool(forKey: "isLoggedIn"))

guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window.windowScene = windowScene

if UserDefaults.standard.bool(forKey: "isLoggedIn") {
guard let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "HomeVC") as? TabController else {
fatalError("Could not instantiate HomeVC!")
}
window.rootViewController = vc
} else {
guard let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "AuthVC") as? AuthViewController else {
fatalError("Could not instantiate HomeVC!")
}
window.rootViewController = vc
}

self.window = window

window.makeKeyAndVisible()
}

}

AppDelegate.swift

//
// AppDelegate.swift
// Created by Don Mag on 3/27/20.
//

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window : UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool {
if #available(iOS 13, *) {
// do only pure app launch stuff, not interface stuff
} else {

print("App Delegate didFinishLaunching... isLoggedIn:", UserDefaults.standard.bool(forKey: "isLoggedIn"))

self.window = UIWindow()

if UserDefaults.standard.bool(forKey: "isLoggedIn") {
guard let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "HomeVC") as? TabController else {
fatalError("Could not instantiate HomeVC!")
}
window?.rootViewController = vc
} else {
guard let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "AuthVC") as? AuthViewController else {
fatalError("Could not instantiate HomeVC!")
}
window?.rootViewController = vc
}

window?.makeKeyAndVisible()

}
return true
}

// MARK: UISceneSession Lifecycle

// iOS 13+ only
@available(iOS 13.0, *)
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
// iOS 13+ only
@available(iOS 13.0, *)
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
}

}

Changing the rootViewController of a UIWindow

It turns out there are two separate issues. 1) I had a retain cycle in Controller A so it was never getting dealloc'd. Secondly, in order to change the root view controller you must remove the windows subviews first (even though the docs suggest otherwise)

Update root view controller after user login + iOS 13 and later

This is how I managed navigation for both the older version and the new version. So when the user has the latest iOS we need to setup root from sceneDelegate and for older version we need to setup root from appDelegate

AppDelegate.swift

class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 13, *) {

} else {
setupRoot()
}
return true
}

// MARK: UISceneSession Lifecycle
@available(iOS 13.0, *)
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}

@available(iOS 13.0, *)
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {

}

func setupRoot() {
//Setup Your Root Here
//window?.rootViewController = objNavigationVC
//window?.makeKeyAndVisible()
}
}

SceneDelegate.swift

@available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window = window
appDelegate.setupRoot()
}
}

set root view controller in main storyboard

change storyboard--> viewcontroller---> attribute inspector---> change presentation from Automatic to Full Screen



Related Topics



Leave a reply



Submit