How to present and dismiss intermediate view controllers?

Hello, everyone!

I'm trying to figure out the best way to correctly present and dismiss view controllers in Swift. I am trying to do this without the Storyboard at all, since I like coding that way. I would like to present a scenario, that I'm in. I have three view controllers; VC1, VC2 and VC3, my root view controller would be VC1. If i use this

present(VC2(), animated: true completion: nil)

in VC1, then that takes me to VC2. Then I call this method again, but where I present VC3. My questions then is, how do I dismiss both VC2 and VC3 at the same time, bringing me back to VC1?

I have tried many ways to do this, but I always end up having to briefly showing the "middle" view controller. Is there any way to avoid this?

Replies

Could you show all the code where you dismiss ?


Did you try to dismiss without animation:

vc2.dismissViewControllerAnimated(false) { }


You write:

present(VC2(), animated: true completion: nil)


So how do you keep a reference to the vc2 ?

why don't you:

let vc2 = VC2()
present(vc2, animated: true completion: nil)

I am pretty new to this, so I might not have done it correctly. I just thought it worked. This is what I have used so far, when presenting and dismissing:

In VC1 I write this:

present(VC2(), animated: true, completion: nil)

And then when I want to dismiss VC2, I write this:

dismiss(animated: true, completion: nil)


I don't make a reference like you did here:

let vc2 = VC2()
present(vc2, animated: true completion: nil)


Should I do that? Why?

So, when you dismiss, you don't know on which object. How could it know it is vc2 ?

if you write

vc2.dismissViewControllerAnimated(false, completion: nil)

then you know dismiss is for vc2.


Could you post larger part of code, that will be easier to help.

Let's not get tangled up in the question of references to VC2, since it has nothing to do with the problem. The problem is that you're Doing It Wrong™.


Well, actually, you're doing it right, but "it" just isn't what you want to do. The documentation for the "present" method says:


Presents a view controller modally.


So, modally. That means that each such presented view controller is "on top of" the presenter, and there is a chain of these which you created link by link, and which you will follow back link by link. That's exactly what's happening, not what you want. Instead, you need to segue from VC2 to VC3 using a suitable segue that replaces one with the other.


This brings up the larger issue. It's fine to try doing things in code instead of using resources like storyboards, but in that case you really, really need to read and understand all of the relevant documentation. That's not a trivial task, and it's harder to do if you've avoided doing things the easy way with resources first.

Yes, I get that, but that isn't really my issue. I have tried to explain what i want to do in the different view controllers, here:


This is the code i would have in VC1. When a button is pressed, i get to VC2.

    @objc func handleShowV2() {
        let secondViewController = SecondViewController()
        present(secondViewController, animated: true, completion: nil)
    }


This is the code i would have in VC2, which again would take me to VC3:

    @objc func handleShowV3() {
        let thirdViewController = ThirdViewController()
        present(thirdViewController, animated: true, completion: nil)
    }


And finally, i would have some code, that would bring me back to VC1, while correctly dealing with dismissal of both VC2 and VC3.

    @objc func handleShowV1() {
        //Somehow dismissing VC2 and VC3.
        
        let firstViewController = FirstViewController()
        present(firstViewController, animated: true, completion: nil)
    }


How would you do this the 'right' way?

Yes, exactly. I am aware of the priciple of presenting view on top of each other.


I think you are right about the storyboards, I think I'll try it out. If you had to do this with segues, how would you do it? You mention a "segue from VC2 to VC3 using a suitable segue that replaces one with the other". What do you mean by that?


EDIT: It seems to be quite easy to do. Just a segue from VC1 to VC2 to VC3, and then an unwind segue from VC3 to VC1, right?

Sorry, that was handwaving to say that I don't know exactly what code you should use. Actually, a segue is tied to a storyboard, so it's no help here. It looks to me that you need some of the methods listed under "Presenting View Controllers" in the documention:


https://developer.apple.com/documentation/uikit/uiviewcontroller


(I'm assuming we're talking about iOS, though you didn't say.) Perhaps "show" or "showDetailViewController(_:sender:)" might be what you want, although you have to consider whether there are supposed to be any animated transitions.


It's also not clear if you might be better off using a navigation controller, which gives you pretty direct control over its children. Or possibly one of the other standard controllers. Or embedded controllers in a custom view controller hierarchy.


Storyboards give you a lot of behavior for free. It's non-trivial to recreate manually, though (I suppose) doable if you're masochistic enough. 🙂

Hi,

So, I was badly wrong, but I will learn some basics.


What difference between :

vc2.dismissViewControllerAnimated(false, completion: nil)

and

dismissViewControllerAnimated(false, completion: nil)


is the last just the equivalent to self.dismissViewControllerAnimated(false, completion: nil) ?

Yeah, I think you are right. I'll have a go with the storyboard approach, at least for segues and such. Thank you for your answers 🙂

i think it is too late to reply to your question but here is working solution. and hope it will help others as well .

`        self.presentingViewController?.presentingViewController?.dismiss(
            animated: true,
            completion: nil
        )`

It's a way to navigate up the chain of presented view controllers and dismiss them one by one. for you it will dissmiss vc2 and vc3 in once .