How to set a new root view controller
Asked Answered
S

25

43

I wonder if its possible to set a new root VC?

My app gets init with a uinavigation controller that has a table view to be the root VC.

Then from the table view I am running another segue to a login window (present modally) If you then login you end up in the red VC/account page. What I want to do now is to set the red VC to be the new root VC of the app, and remove all underlying VC's. So that I can show a menu button/icon instead of a "Back" button

I have found this but I dont understand how to use it:

let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
        let yourViewController: ViewController = storyboard.instantiateViewControllerWithIdentifier("respectiveIdentifier") as! ViewController

        let navigationController = self.window?.rootViewController as! UINavigationController
        navigationController.setViewControllers([yourViewController], animated: true)

But I cannot get it to work. So is it possible to make the red vc in the picture act as the new root VC.

enter image description here

Sumpter answered 27/10, 2015 at 17:8 Comment(0)
R
49

Swift 4.2

May be you should try this

let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
let redViewController = mainStoryBoard.instantiateViewController(withIdentifier: "respectiveIdentifier") as! ViewController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = redViewController
Rhapsodist answered 28/10, 2015 at 12:33 Comment(3)
well... it doesn't crash. but for me, nothing happens here. no new view will be loadedRowlock
My problem is the new view controller replace the old one. But the view controller stacks still be kept. Don't know where those view controller stacks coming from.Fecit
For ppl who used SceneDelegate, refer #56589343Jacobine
C
36

Swift 4, 5, 5.1

let story = UIStoryboard(name: "Main", bundle:nil)
let vc = story.instantiateViewController(withIdentifier: "NewViewController") as! NewViewController
UIApplication.shared.windows.first?.rootViewController = vc
UIApplication.shared.windows.first?.makeKeyAndVisible()
Calm answered 2/11, 2019 at 14:29 Comment(2)
Thank you so much. !! It's help me a lots.Curkell
The makeKeyAndVisible do the trick! ThanksSyncopation
N
23

Swift 3 Update:-

let testController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "testController") as! TestController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = testController
Nursery answered 9/3, 2017 at 11:46 Comment(2)
Works for me ThanksHeliotherapy
This does not remove the previous view from underneath...Calen
B
9

Swift 4 Answer

let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "YourViewController") as! YourViewController
let navigationController = UINavigationController(rootViewController: nextViewController)            
let appdelegate = UIApplication.shared.delegate as! AppDelegate
appdelegate.window!.rootViewController = navigationController
Boche answered 5/1, 2018 at 19:30 Comment(2)
how can u instantiate root view controller with navigation bar...I'm not getting itSwordcraft
what to do in xcode 11. there is no window property in appdelegateNucleolated
A
8

UINavigationController has a viewControllers property, which is a NSArray, And you can replaced it with your own NSArray of view controllrs.

This can be done as show in below sample code.

let newViewController = self.storyboard?.instantiateViewControllerWithIdentifier("YourViewControllerollerID") as! YourViewController
let customViewControllersArray : NSArray = [newViewController]
navigationController?.viewControllers = customViewControllersArray as! [UIViewController]

And if you want to show this new root view controller you can just call UINavigationController's popToRootViewController() method.

navigationController?.popToRootViewControllerAnimated(true)
Atomizer answered 15/12, 2016 at 5:7 Comment(0)
T
5

In order to get the code snippet from the original question to work, I had to make a change to the third line

let navigationController = self.navigationController!

I am using this code in an @IBAction in the view controller that precedes the new root view controller.

Using the original code, I was receiving an error saying that my view controller had no member called window. After looking at the documentation, I could find no property named window. I'm wondering if the original block of code above was intended to be used inside a UINavigationController file.

Here is the block in its entirety.

let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let todayViewController: TodaysFrequencyViewController = storyboard.instantiateViewControllerWithIdentifier("todaysFrequency") as! TodaysFrequencyViewController    
let navigationController = self.navigationController!
navigationController.setViewControllers([todayViewControl ler], animated: true)
Taro answered 11/5, 2016 at 21:29 Comment(0)
T
4

I wonder if its possible to set a new root VC?

Yes, it's possible. How you do it depends on the context...

My app gets init with a uinavigation controller that has a table view to be the root VC.

There are actually two things that are commonly called the "root view controller":

  • UIWindow has a rootViewController property, which is writeable.

  • UINavigationController has no rootViewController property, but it does have an initializer called -initWithRootViewController:. You can set the nav controller's "root" view controller by setting it's viewControllers property.

It sounds like you're trying to change the window's root view controller, but the code you show only changes the nav controller's viewControllers property. Try setting the window's rootViewController property directly. Understand, however, that if you take that approach then the navigation controller will go away too. If you want to keep the nav controller, on the other hand, go with your current approach.

But I cannot get it to work. So is it possible to make the red vc in the picture act as the new root VC.

More information here would be helpful. What do you mean by "cannot get it to work"? What happens, and what do you expect to happen?

Titfer answered 27/10, 2015 at 17:41 Comment(6)
After your explination I think I should change the window root instead. But if I do that can I then add a new uinavigation controller and also remove all old views? Eg VC's opened by modal segueSumpter
In story board I have also checked "Is initial VC" on the navigation controllerSumpter
You can, but if your nav controller is already the window's root view controller, and if you want a nav controller and don't need the current stack of view controllers, then why not keep the nav controller you have? IOW, your current approach is reasonable -- if it's not working, you should provide more info about that. How do you know it doesn't work? What does navigationController.viewControllers show after you set it?Titfer
I get the error "MyViewController" has no member named view. Where/how exactly should I implement the code when making the transition to the new VC that should become the new root?Sumpter
Every view controller has a view property, so if you're getting that message the object you're messaging is not a proper view controller. Make sure that your ViewController class is derived from UIViewController, for a start.Titfer
import UIKit class AccountViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } } I have also set the VC to have thet class in storyboardSumpter
C
3

For swift 4.0.

In your AppDelegate.swift file in didfinishedlaunchingWithOptions method, put the following code.

var window: UIWindow?


func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    window = UIWindow(frame: UIScreen.main.bounds)
    window?.makeKeyAndVisible()

    let rootVC = MainViewController() // your custom viewController. You can instantiate using nib too. UIViewController(nib name, bundle)

    let navController = UINavigationController(rootViewController: rootVC) // Integrate navigation controller programmatically if you want
    window?.rootViewController = navController

    return true
}

Hope it will work just fine.

Cumbrous answered 11/10, 2018 at 5:22 Comment(0)
M
3

For Swift 5 Users you can do this way and this will definitely work for you.

var window: UIWindow?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
    manageLoginSession()
    guard let _ = (scene as? UIWindowScene) else { return }
}

func manageLoginSession() {
    guard let window = window else {return}
    if UserDefaults.standard.bool(forKey: "_key_AlreadyLogin") == true {
        window.rootViewController = UIStoryboard(name: "Dashboard", bundle: nil).instantiateInitialViewController()
    }else{
        window.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
    }
}
Metalline answered 20/6, 2021 at 6:49 Comment(0)
A
2

You can use this code when you click the login button :-

let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var vc = mainStoryboard.instantiateViewControllerWithIdentifier("respectiveIdentifier") as ViewController  
UIApplication.sharedApplication().keyWindow.rootViewController = vc
Aphrodisia answered 28/10, 2015 at 12:8 Comment(0)
Z
2

How and from where are you presenting redVC?

You could make it root view controller of your UINavigationController, so you would still have ability to push and pop view controllers.

self.navigationController?.viewControllers = [self];
Zoes answered 11/5, 2016 at 21:47 Comment(0)
B
2

You can use this bit of code:

let newViewController = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController

let customViewControllersArray : NSArray = [newViewController]
self.navigationController?.viewControllers = customViewControllersArray as! [UIViewController]
self.navigationController?.pushViewController(newViewController, animated: true)
Besmear answered 7/9, 2017 at 10:20 Comment(2)
Can you please explain why your code helps answering the OP's question?Haile
if HomeViewController is your root view controller so you can set it as root view controller by using pushViewControllerBesmear
D
2

If you need to set rootViewController with some animations, here is the code:

guard let window = UIApplication.shared.keyWindow else {
    return
}

guard let rootViewController = window.rootViewController else {
    return
}

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "MainTabbar")
vc.view.frame = rootViewController.view.frame
vc.view.layoutIfNeeded()

UIView.transition(with: window, duration: 0.3, options: .transitionCrossDissolve, animations: {
    window.rootViewController = vc
}, completion: { completed in
    // maybe do something here
})
Disenthrall answered 4/2, 2019 at 15:50 Comment(0)
T
2

For Swift 5 and above this may work for you.

if let delegate = UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate {
        
        delegate.window?.rootViewController = newViewController
        delegate.window?.makeKeyAndVisible()
        
}
Terena answered 2/5, 2022 at 11:37 Comment(0)
R
2

Swift 4,5

Use this below code for RootViewController

    let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
    let mainViewController = storyBoard.instantiateViewController(withIdentifier: "MainViewController") as! MainViewController
    let navigationController = UINavigationController(rootViewController: nextViewController)            
    if let window = UIApplication.shared.delegate?.window {
        window?.rootViewController = navigationController
     }
Rain answered 2/7, 2022 at 17:40 Comment(0)
N
0

once you are in the vc that you want to set as root, just add in your viewDidLoad:

let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = self

*As per best practice you should check if it is already the root, if not execute the code above.

Nostomania answered 19/8, 2017 at 18:5 Comment(0)
F
0

Swift 3 AppDelegate file:::

@IBAction func btnGoBack(_ sender: UIButton){

   self.goToHomeVC()
}

func goToHomeVC(){

    let storyboard = UIStoryboard(name: "Main", bundle: nil)

    let viewController = storyboard.instantiateViewController(withIdentifier :"HomeVC") as! HomeVC

    let navController = UINavigationController.init(rootViewController: viewController)

    if let window = self.appDelegate.window, let rootViewController = window.rootViewController {

        var currentController = rootViewController

        while let presentedController = currentController.presentedViewController {

            currentController = presentedController
        }

        currentController.present(navController, animated: true, completion: nil)
    }
}
Frisch answered 4/9, 2017 at 11:5 Comment(0)
J
0

Swift 4

 let storyBoard = UIStoryboard(name: "Your_Storyboard_Name", bundle:Bundle.main)
 self.window = UIWindow(frame: UIScreen.main.bounds)
 let yourVc = storyBoard.instantiateViewController(withIdentifier: "YourIdentifier") as? YourViewController
 if let window = window {
     window.rootViewController = yourVc
 }
 window?.makeKeyAndVisible()
Jabot answered 26/10, 2017 at 9:8 Comment(0)
H
0

You can try out this code

func switchRootViewController(rootViewController: UIViewController, animated: Bool, completion: (() -> Void)?) {
    guard let window = UIApplication.shared.keyWindow else { return }
    if animated {
        UIView.transition(with: window, duration: 0.5, options: .transitionCrossDissolve, animations: {
            let oldState: Bool = UIView.areAnimationsEnabled
            UIView.setAnimationsEnabled(false)
            window.rootViewController = rootViewController
            UIView.setAnimationsEnabled(oldState)
        }, completion: { (finished: Bool) -> () in
            if (completion != nil) {
                completion!()
            }
        })
    } else {
        window.rootViewController = rootViewController
    }
}
Hypermetropia answered 8/1, 2018 at 14:40 Comment(0)
D
0

Link to a related question

This answer applies to usage of an existing ViewController from somewhere in the current stack without instantiating and reconfiguring a new controller.

The documentation says: The root view controller provides the content view of the window. Assigning a view controller to this property (either programmatically or using Interface Builder) installs the view controller’s view as the content view of the window. The new content view is configured to track the window size, changing as the window size changes. If the window has an existing view hierarchy, the old views are removed before the new ones are installed.

Just as the documentation says: It removes all views in the stack if the rootViewController is exchanged. No matter what's with the controller. So remove the ViewController from the stack to assure its view won't be removed.

This resulted in my case in the following solution:

if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
    guard let pageVC = self.onboardingDelegate as? OnboardingPageViewController else { return } // my current stack is in a pageViewController, it also is my delegate
    let vc = self // holding myself
    pageVC.subViewControllers.removeLast() // removing myself from the list
    pageVC.setViewControllers([pageVC.subViewControllers[0]], direction: .forward, animated: false, completion: nil) // remove the current presented VC
    appDelegate.window?.rootViewController = vc
    vc.onboardingDelegate = nil
    appDelegate.window?.makeKeyAndVisible()
}
Disjuncture answered 29/8, 2018 at 13:35 Comment(0)
H
0

Any view controller you want to set root just call the below function like

  UIApplication.shared.setRootVC(vc)

  extension UIApplication {

    func setRootVC(_ vc : UIViewController){

        self.windows.first?.rootViewController = vc
        self.windows.first?.makeKeyAndVisible()

      }
  }
Hamish answered 12/1, 2020 at 12:19 Comment(0)
A
0

Just write this and you are good to go.

let sb = UIStoryboard(name: "Main", bundle: nil)
let VC = sb.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
let navRootView = UINavigationController(rootViewController: VC)
self.present(navRootView, animated: true, completion: nil)
Alphorn answered 31/7, 2020 at 16:13 Comment(2)
Please explain why this works. What is the issue and why this resolves it.Medullary
UInavigationController has a property which allows user to set its root view controller so here we are setting our VC to the top of current navigation stack and making it a root view controllerAlphorn
M
0

This is how you can set the nib as root view controller.

 let vc = HomeViewController(nibName: "HomeViewController", bundle: nil)
 window = UIWindow(frame: UIScreen.main.bounds)
 window?.rootViewController = vc
 window?.makeKeyAndVisible()
Metalline answered 18/8, 2020 at 1:29 Comment(0)
P
0

Swift 4,5 and above If you Use Multiple or single Story board you want to set Different Root view controller of Navigation Controler then I use This Method: In My case StoryBaord Name is Auth.

    func setRootToLogin(transition :CATransition) {
    let storyboard = UIStoryboard(name: "Auth", bundle: nil)
    let loginNav =  storyboard.instantiateViewController(withIdentifier: "AuthNavigationController") as! UINavigationController
    window.set(rootViewController: loginNav, withTransition: transition)
    let vc =  window.rootViewController as! UINavigationController
    let loginvc = LoginViewController.instantiateAuth()
    vc.setViewControllers([loginvc], animated: true)
}
Penelopa answered 18/6, 2021 at 13:25 Comment(0)
B
0

Swift 5+ IOS 13+

extension UIViewController {
    var appDelegate: AppDelegate {
         return UIApplication.shared.delegate as! AppDelegate
    }

    var sceneDelegate: SceneDelegate? {
        guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,             
        let delegate = windowScene.delegate as? SceneDelegate else { return nil }
            return delegate
        }
}



//Calling Method
    let storyBoard : UIStoryboard = UIStoryboard(name: "Home", bundle:nil)
    if let navigationController = storyBoard.instantiateViewController(withIdentifier: "NavigationHomeViewController") as? UINavigationController {
        self.sceneDelegate?.window?.rootViewController = navigationController
    }
Bothnia answered 9/7 at 13:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.