简体   繁体   中英

Dismiss or remove previous modally presented view controller as soon as the next one appear modally

My target include a lot view need to present different view modally base on each user action. Here what I want to do to get cleaner view hierarchy and better user experience.

  1. Root View Controller present First View Controller modally
  2. When I clicked button on the First View Controller, then the Second View Controller appear modally over it.
  3. As soon as the Second View Controller did appear, I want to dismiss or remove the first one from view hierarchy.

Can I do that? If so , how should i do it?

If not , what is the right way to solve this out cause I will present many modally presented view controllers over each view. I think even if I want to dismiss current view, the previous one will still remain appear when current one dismiss.

UPDATE :

VC1 (Root) > VC 2 (which was present modally) > VC 3 (which was present modally over VC 2)

When i dismiss VC3 , the VC2 is still on view memory. So, I don't want to appear VC2 as soon as I dismiss VC3 and instead I want to see VC1 by removing or dismissing VC2 from view hierarchy.

在此输入图像描述

WANT : At the image, when I dismiss the blue,I don't want see the pink in my view memory and I want to remove it as soon as the blue one appear.

That's what i want to do.

Any Help?Thanks.

So, let's assume that you have a storyboard similar to:

在此输入图像描述

What should happens is:

  • Presenting the the second ViewController (from the first ViewController).
  • Presenting the the third ViewController (from the second ViewController).
  • dismissing to the first ViewController (from the third ViewController).

In the third ViewController button's action:

@IBAction func tapped(_ sender: Any) {
    presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)
}

As you can see, by accessing the presentingViewController of the current ViewController, you can dismiss the previous hierarchy of the view controllers:

The view controller that presented this view controller.

By implementing presentingViewController?.presentingViewController? that means that: the presented of the presented current ViewController :)

It might seem a little bit confusing, but it is pretty simple.

So the output should be like (I added background colors to the viewControllers -as vc1: orange, vc2: black and vc3: light orange- to make it appears clearly):

在此输入图像描述

EDIT:

If you are asking to remove the ViewController(s) in the middle (which in this example the second ViewController), dismiss(animated:completion:) does this automatically:

If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack. When this happens, only the top-most view is dismissed in an animated fashion; any intermediate view controllers are simply removed from the stack. The top-most view is dismissed using its modal transition style, which may differ from the styles used by other view controllers lower in the stack.

Referring to what are you asking:

I think even if I want to dismiss current view, the previous one will still remain appear when current one dismiss.

I think that appears clearly on the UI (and I find it ok), but as mentioned in the dismiss documentation discussion, both the third and the second will be removed from the stack. That's the right way.

What you want is an "unwind segue":

https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/UsingSegues.html#//apple_ref/doc/uid/TP40007457-CH15-SW8

https://developer.apple.com/library/archive/technotes/tn2298/_index.html

It allows you to dismiss multiple view controllers at the same time, without having to know how many there are in the stack.

In VC1 you would implement an IBAction called (for instance) unwindToRoot . Then in the storyboard for VC3, you wire up your Done button to the Exit object and choose the unwindToRoot action.

When that button is pressed, the system will dismiss all the view controllers it needs to bring you back to VC1.

This is better than calling presentingViewController?.presentingViewController?.dismiss() , because VC3 doesn't need to know anything about the view controller hierarchy underneath it.

Here is my opinion in different perspective,

  1. Root View Controller present Second View Controller
  2. Add FirstView onto Second View
  3. Dismiss FirstView Controller when button pressed.

在此输入图像描述

Second View Controller,

class ViewController: UIViewController, FirstViewControllerProtocol {

    weak var firstViewController: FirstViewController?

    override func viewDidLoad() {
        super.viewDidLoad()

        print("Not initiated: \(firstViewController)")

        firstViewController  = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "FirstViewController") as? FirstViewController
        addChildViewController(firstVC!)
        firstViewController?.delegate = self
        view.addSubview((firstViewController?.view)!)

        print("Initiated: \(firstViewController)")
    }

    func dismiss() {
        firstViewController?.view.removeFromSuperview()
        firstViewController?.removeFromParentViewController()

    }
}

FirstViewController,

protocol FirstViewControllerProtocol {
    // Use protocol/delegate to communicate within two view controllers
    func dismiss() 
}

class FirstViewController: UIViewController {

    var delegate: FirstViewControllerProtocol?

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func dismiss(_ sender: Any) {
        delegate?.dismiss()
    }

    deinit {
        print("BYE")
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM