Emre Önder
Emre Önder

Reputation: 2537

Dismiss two UIViewController at once without animation

I have a stack of UIViewControllers like A -> B -> C. I want to go back to controller A from C. I'm doing it with below code:

DispatchQueue.global(qos: .background).sync {
// Background Thread
DispatchQueue.main.async {
    self.presentingViewController?.presentingViewController?.dismiss(animated: false, completion: {
    })}
}

It works but controller B seen on screen although I set animated to false. How can I dismiss two UIViewControllers without showing the middle one (B)?

P.S: I can't just directly dismiss from root controller and also I can't use UINavigationController

I searched the community but can't find anything about the animation.

Dismiss more than one view controller simultaneously

Upvotes: 1

Views: 888

Answers (2)

Gkolunia
Gkolunia

Reputation: 92

I've created example for dismissing B controller before showing C controller. You can try it.

    let bController = ViewController()
    let cController = ViewController()

    aController.present(bController, animated: true) {

        DispatchQueue.main.asyncAfter(wallDeadline: .now()+2, execute: {

            let presentingVC = bController.presentingViewController

            bController.dismiss(animated: false, completion: {

                presentingVC?.present(cController, animated: true, completion: nil)

            })
        })

    }

But on my opinion solution with using navigation controller would be the best for the case. For example you can put just B controller into navigation controller -> present the navController onto A controller -> then show C inside the navController -> then dismiss from C controller whole navController -> And you will see A controller again. Think about the solution too.

Another solution

I've checked another solution. Here extension which should solve your problem.

extension UIViewController {

    func dissmissViewController(toViewController: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
        self.dismiss(animated: flag, completion: completion)
        self.view.window?.insertSubview(toViewController.view, at: 0)
        dissmissAllPresentedControllers(from: toViewController)
        if toViewController.presentedViewController != self {
            toViewController.presentedViewController?.dismiss(animated: false, completion: nil)
        }
    }

    private func dissmissAllPresentedControllers(from rootController: UIViewController) {
        if let controller = rootController.presentedViewController, controller != self {
            controller.view.isHidden = true
            dissmissAllPresentedControllers(from: controller)
        }
    }

}

Usage

let rootController = self.presentingViewController!.presentingViewController! //Pointer to controller which should be shown after you dismiss current controller
self.dissmissViewController(toViewController: rootController, animated: true) 

// All previous controllers will be dismissed too, // but you will not see them because I hide them and add to window of current view.

But the solution I think may not cover all your cases. And potentially there can be a problem if your controllers are not shown on whole screen, all something like that, because when I simulate that transition I don't consider the fact, so you need to fit the extension maybe to your particular case.

Upvotes: 0

Ratul Sharker
Ratul Sharker

Reputation: 8021

Try this.

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

Created a sample storyboard like this

enter image description here

The yellow view controller is type of ViewController and the button action is as follows

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

Output

enter image description here

Upvotes: 2

Related Questions