How to set self as new window.rootViewController?
Asked Answered
P

5

0

I want to set one of my ViewControllers from my navigation stack as my apps window.rootViewController while I am somewhere in another controller's navigation stack. That other controller is currently my window.rootViewController. Therefore I use this code:

if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
    appDelegate.window?.rootViewController = self
}

This results in just a black screen. When I instantiate a new ViewController from Storyboard it's working fine, but all entries are gone. I don't want to reconfigure the new viewController, if that is avoidable.

Update A
I put self to a strong reference in appDelegate to check that, because I thought it's view was unloaded somewhere on the way, but it still resulted in a black screen.

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.

Clarification: I don't want to instantiate a new VC, I want to use the current one, without putting a navigationController around it, it should not be necessary. I'm sure it is possible without any workaround, I am just missing something here.

Plasmasol answered 29/8, 2018 at 11:3 Comment(5)
So you are trying to take a UIViewController which is on another UIViewController-stack already and put it on its own stack?Prostration
Possible duplicate of Set rootViewController of UINavigationController by method other than initWithRootViewControllerDisgorge
No duplicate. I updated my question to clarify for both comments.Plasmasol
Possible duplicate of Swift ios set a new root view controllerCarmencita
Definitely related, but not duplicate, the other ticket is about any possible exchange of the rootV with a new ViewController, which appears to be in the stack, and the results there said to instantiate a new one. I want to use the exact same VC as currently presented as the rootVC, without instantiating new or wrapping it in another stack.Plasmasol
P
1

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 I had to remove self from the stack to assure my view won't be removed. This resulted in 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()
    }

And it is working fine, just like I wanted.

Plasmasol answered 29/8, 2018 at 13:1 Comment(3)
I have similar case but with UINavigationController. My rootViewController has UINavigationController with A and B viewcontollers. I wanna set B as rootViewController without reload and deinit UINavigationController with A. This one logic doesn't work. if let appDelegate = UIApplication.shared.delegate as? AppDelegate { let vc = self self.navigationController?.viewControllers = [] appDelegate.window?.rootViewController = vc appDelegate.window?.makeKeyAndVisible() } Do you have any suggestions?Courtesy
Note that even thought it claims that all are removed, it might not really be. Double check and make sure there is no memory leaks (dealloc/deinit are called for all your views).Integrand
@Integrand Yes, that may be, but in my project it didn't appear to cause issues. But checking that for your project is important, obviously.Plasmasol
S
2

To be more precise, you can use navigation controller with your view controller

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let appDelegate = UIApplication.shared.delegate as? AppDelegate
let initialViewController = storyboard.instantiateViewController(withIdentifier: "yourViewController") as! yourViewController
let nvc: UINavigationController = UINavigationController(rootViewController: initialViewController) 
appDelegate?.window?.rootViewController = nvc
appDelegate?.window?.makeKeyAndVisible()
Stuff answered 29/8, 2018 at 11:13 Comment(1)
Thank you for your example, I updated my question for clarification (I didn't want to create a new instance or wrap it in another navigation stack or similar) and I added an answer with explanation from the documentation for detail. My view was unloaded because it was still present in the old navigation stack, once I removed it there first and then used it for the new rootVC, it worked perfectly fine.Plasmasol
P
1

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 I had to remove self from the stack to assure my view won't be removed. This resulted in 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()
    }

And it is working fine, just like I wanted.

Plasmasol answered 29/8, 2018 at 13:1 Comment(3)
I have similar case but with UINavigationController. My rootViewController has UINavigationController with A and B viewcontollers. I wanna set B as rootViewController without reload and deinit UINavigationController with A. This one logic doesn't work. if let appDelegate = UIApplication.shared.delegate as? AppDelegate { let vc = self self.navigationController?.viewControllers = [] appDelegate.window?.rootViewController = vc appDelegate.window?.makeKeyAndVisible() } Do you have any suggestions?Courtesy
Note that even thought it claims that all are removed, it might not really be. Double check and make sure there is no memory leaks (dealloc/deinit are called for all your views).Integrand
@Integrand Yes, that may be, but in my project it didn't appear to cause issues. But checking that for your project is important, obviously.Plasmasol
R
1

You have to remove it from the navigation controller before setting it as the root view controller:

if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
    willMove(toParentViewController: nil)
    view.removeFromSuperview()
    removeFromParentViewController()
    appDelegate.window?.rootViewController = self
}

NOTE: this is not the best approach, you should revisit your navigation stack.

Rocambole answered 29/8, 2018 at 13:1 Comment(0)
S
0
    if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
        let objLoginViewController = UIStoryboard(name: "Main", bundle:nil).instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
        appdelegate.window?.rootViewController = objLoginViewController
        appdelegate.window?.makeKeyAndVisible()
   }

if you don't want to instantiate new controller then you can find that controller from your navigation stack and assign into rootViewController

Shanika answered 29/8, 2018 at 11:5 Comment(1)
I tried that, but the behavior was the same, just a black screen. I updated my question and tried using a strong reference on my vc in the appDelegate, but the view still is black.Plasmasol
A
0

You are on the right track, but just add your self to NavigationController and then set it as rootViewController and makeKeyAndVisible the window.

if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
        let nav = UINavigationController(rootViewController: self)
        // IF YOU DON'T WANT NAVIGATION BAR, set it hidden
        nav.navigationBar.isHidden = true
        appDelegate.window?.rootViewController = nav
        appDelegate.window?.makeKeyAndVisible()
}

Try and share your results!

Apprentice answered 29/8, 2018 at 11:32 Comment(5)
Can you explain, why I need the navigationController to hold self? Why is self on its own not enough?Plasmasol
Probably, when you set the new rootViewController to the window object, it nil out the existing viewController and thus you see the black screen. As I know, class objects are passed by reference thus setting the rootViewController to NavigationController should also nil out, but anyhow it's holding the viewController and can be presented for display.Apprentice
Yes, you are heading the right direction, I added a new answer, explaining why my screen resulted in a black page. My view was unloaded by removing the old rootViewController, the documentation says it all.Plasmasol
okay got it completely from your answer. thanks and +1 for the proper explanation.Apprentice
You can mark Your answer as a correct answer to help others with information.Apprentice

© 2022 - 2024 — McMap. All rights reserved.