0

I know there are two ways to show a new UIViewController in Swift. There are:

self.present(controllerToPresent, animated: true, completion: nil) 

and

self.performSegue(withIdentifier: "controllerToPresent", sender: nil) 

But both of them show the new UIViewController on top of the other. Assume I don't want to stack controllers on each other rather than just switch the controllers. The new presented UIViewController should be the new root-controller. An example for this would be a login page. Once a user logged in I don't use the login-controller anymore, so why would I like to stack the new controller on top of it. So the question is, is there a method to switch (not stacking) UIViewControllers?

Furthermore I want to know what happens to the memory that was allocated for a new instance of an UIViewController when I use one of these two functions above. I'm not sure if at some time ARC frees the memory or if I run out of memory at some time calling these functions too often.

nasxlf
  • 45
  • 7
  • Possible duplicate of [Swap rootViewController with animation?](https://stackoverflow.com/questions/41144523/swap-rootviewcontroller-with-animation) – Sandeep Bhandari Nov 19 '17 at 15:56

2 Answers2

0

Specifically, for the case of (as you mentioned as an example of what are looking for):

An example for this would be a login page. Once a user logged in I don't use the login-controller anymore

You would need to determine the desired rootViewController in the app delegate, example:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // let's assume that you impelemnted to logic of how to determine whether the user loggedin or not,
    // by using 'isLoggedin' flag:

    if let wnwrappedWindow = self.window {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)

        if isLoggedin {
            let rootHomeVC = storyboard.instantiateViewControllerWithIdentifier("HomeViewController") as! HomeViewController
            //...
            wnwrappedWindow.rootViewController = rootHomeVC
        } else {
            let rootloginVC = storyboard.instantiateViewControllerWithIdentifier("HomeViewController") as! HomeViewController
            //...
            wnwrappedWindow.rootViewController = rootloginVC
        }
    }

    return true
}

In case of you want to change the root view controller in the login view controller, you could implement the following code when it is a success login:

let ad = UIApplication.shared.delegate as! AppDelegate
if let window = ad.window {
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let rootHomeVC = storyboard.instantiateViewControllerWithIdentifier("HomeViewController") as! HomeViewController
    //...
    window.rootViewController = rootHomeVC
}
Ahmad F
  • 30,560
  • 17
  • 97
  • 143
  • What if the user is not logged in and then uses the login screen to login. Wouldn't the login screens ViewController always be on the stack (as the first element) till the app is terminated? The login screen should never be accessed after someone logged in. – nasxlf Nov 19 '17 at 13:15
  • That's the purpose of determining the root view! please check the update. – Ahmad F Nov 19 '17 at 13:24
  • You only set a root controller when the app started. But what if I want to change it during runtime? I want that a new `UIViewController` is present without presenting it modally because I don't want dismiss the new controller (or go back). My assumption is that if I call one of the two mentioned functions (`present` and `performSegue`) too often the stack will unnecessarily get another element on top which could be negative for the performance of the app – nasxlf Nov 19 '17 at 13:50
  • What are you looking for would be exactly what I mentioned in the second code snippet, the whole would be changed without showing any animation :) you should implement it after login success, have you tried it btw? – Ahmad F Nov 19 '17 at 13:52
  • Okay, but what if I want to make an animation? Should I first call `present` (or `performSegue`) **and then** set my `HomeViewController` as my new root controller or is there another way to animated it? If there is no other way than calling `present` and then setting the `HomeViewController` as my new ViewController will ARC eventually free the memory of the login screen ViewController? Because imagine someone will log in and log out 1000+ times consecutively and you always instantiate a new ViewController without free the memory your memory will eventually run out of space. – nasxlf Nov 19 '17 at 15:12
0

There are many ways to do what you want...

One approach, since you comment that you want animation:

  1. Use a "container" view as your "root" view controller
  2. On launch, check if user is "logged in"
  3. If not logged in, instantiate "login" view controller, and use addChildViewController() and addSubview() to show your "login" view.
  4. Else, if already logged in on launch, instantiate "main" view controller, and use addChildViewController() and addSubview() to show your "main" view.

In the case of 3, when user completes the log=on process, instantiate "main" view controller, and use addChildViewController()... then addSubview(), but add it hidden and/or off-screen, and use a UIView animation to replace the "login" view with the "main" view... then remove the login view and controller from memory (removeFromSuperview, removeFromParentViewController, set vc reference to nil, etc).

If at some point you want to "log-off" and return to the login screen, do the same thing... instantiate loginVC, addsubview, animate subview, remove mainVC.

DonMag
  • 69,424
  • 5
  • 50
  • 86