If I present a ViewController programmatically without using a Navigation Controller, does the new VC "replace" the old one, or does it stack on top?

Viewed 239

I'm new to iOS.

I have an app where the path through the app can vary depending on the configuration I fetch from an API. Because of this, I don't use segues because I would need to create a segue from each ViewController (VC) to EVERY other VC. It creates a mess of segues that I don't want. So Instead I navigate from screen to screen like this:

func navigate(to viewController: String) {
    let storyboard = UIStoryboard(name: K.mainStoryBoard, bundle: nil)
    let nextVC = storyboard.instantiateViewController(identifier: viewController)
    self.present(nextVC, animated: true, completion: nil)
}

My question is this: If I would have embedded my VCs in a NavigationController I know it would have created a stack. When I get to the last screen I would call func popToRootViewController(animated: Bool) -> [UIViewController]? and start from the beginning. However, I don't use a NavigationController. So, does that mean that when I present the next VC it replaces the previous one or does it stack on top of the previous one? I'm trying to prevent memory leaks so I want to make sure I don't keep stacking the VCs on top of each other until my app runs out of memory and crashes.

Thanks in advance

Edit

So, in my final VC I created an unwind segue. And I call it like this: performSegue(withIdentifier: "unwindToMain", sender: self)

and In my first VC (the initial VC in my app) I write this:

 @IBAction func unwind( _ seg: UIStoryboardSegue) {

 }

Everything works fine the first trip through the app. The last VC unwinds back to the fist VC. The problem is now that when I try to run through the app again (starting from VC 1 and then going to the next one) I now get this error:

MyApp[71199:4203602] [Presentation] Attempt to present <MyApp.DOBViewController: 0x1038760c0> on <MyApp.ThankYouViewController: 0x112560c30> (from <MyApp.ThankYouViewController: 0x112560c30>) whose view is not in the window hierarchy.

To make sense of this, DOBViewController would be the second VC I want to go to from the MainVC. ThankYouViewController is my last VC. It looks as if it isn't completely removed from the stack. Can anyone tell me what's going on?

2 Answers

does that mean that when I present the next VC it replaces the previous one or does it stack on top of the previous one?

The new one stacks on top of the previous one.

When you present a view controller, like

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

The one you called .present on (self in this case) becomes presentingViewController for the nextVC instance.

The one you presented (nextVC in this case) becomes presentedViewController for the self instance.

Here is a very simple, basic example...

The controllers are setup in Storyboard, each with a single button, connected to the corresponding @IBAction.

The DOBViewController has its Storyboard ID set to "dobVC".

The ThankYouViewController has its Storyboard ID set to "tyVC".

MainVC is embedded in a navigation controller (in Storyboard) and the navigation controller is set to Initial View Controller:

class MainVC: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        navigationController?.setNavigationBarHidden(true, animated: false)
    }
    
    @IBAction func pushToDOB(_ sender: Any) {
        if let vc = storyboard?.instantiateViewController(withIdentifier: "dobVC") as? DOBViewController {
            navigationController?.pushViewController(vc, animated: true)
        }
    }
    
}

class DOBViewController: UIViewController {
    
    @IBAction func pushToTY(_ sender: Any) {
        if let vc = storyboard?.instantiateViewController(withIdentifier: "tyVC") as? ThankYouViewController {
            navigationController?.pushViewController(vc, animated: true)
        }
    }
    
}

class ThankYouViewController: UIViewController {
    
    @IBAction func popToRoot(_ sender: Any) {
        navigationController?.popToRootViewController(animated: true)
    }
    
}
Related