single function to dismiss all open view controllers

IosSwiftUiviewcontrollerDismiss

Ios Problem Overview


I have an app which is a single view application. I have a navigation controller linked up to all child controllers from the root view controller.

In each child controller, I have a logout button. I'm wondering if I can have a single function I can call which will dismiss all the controllers which have been open along along the way, no matter which controller was currently open when the user presses logout?

My basic start:

func tryLogout(){
     self.dismissViewControllerAnimated(true, completion: nil)
     let navigationController = UINavigationController(rootViewController: UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("LoginViewController") )
     self.presentViewController(navigationController, animated: true, completion: nil)
}

I am looking for the most memory efficient way of carrying out this task. I will put my logout function in a separate utils file, but then I can't use self. And I still have the issue of knowing which controllers to dismiss dynamically.

Update Pop to root view controller has been suggested. So my attempt is something like:

func tryLogout(ViewController : UIViewController){
     print("do something")
     dispatch_async(dispatch_get_main_queue(), {
         ViewController.navigationController?.popToRootViewControllerAnimated(true)
         return
     })
 }

Would this be the best way to achieve what I'm after?

Ios Solutions


Solution 1 - Ios

You can call :

self.view.window!.rootViewController?.dismiss(animated: false, completion: nil)

Should dismiss all view controllers above the root view controller.

Solution 2 - Ios

Updated answer for Swift 4 and swift 5

UIApplication.shared.keyWindow?.rootViewController?.dismiss(animated: true, completion: nil)

and when you use navigationController

self.navigationController?.popToRootViewController(animated: true)

Solution 3 - Ios

Works for Swift 4 and Swift 5

To dismiss any unwanted residue Modal ViewControllers, I used this and worked well without retaining any navigation stack references.

UIApplication.shared.keyWindow?.rootViewController?.dismiss(animated: false, completion: nil)

self.view.window! did crash for my case possibly because its a Modal screen and had lost the reference to the window.

Solution 4 - Ios

Swift3

navigationController?.popToRootViewControllerAnimated(true)

Solution 5 - Ios

Take a look at how unwind segues work. Its super simple, and lets you dismiss/pop to a certain viewcontroller in the heirarchy, even if it consists of a complex navigation (nested pushed and or presented view controllers), without much code.

Here's a very good answer (by smilebot) that shows how to use unwind segues to solve your problem https://stackoverflow.com/a/27463286/503527

Solution 6 - Ios

To dismiss all modal Views.

Swift 5


view.window?.rootViewController?.dismiss(animated: true, completion: nil)

Solution 7 - Ios

If you have a customed UITabbarController, then try dismiss top viewController in UITabbarController by:

class MainTabBarController: UITabBarController {

    func aFuncLikeLogout() {

        self.presentedViewController?.dismiss(animated: false, completion: nil)
        
        //......
    }

}

Solution 8 - Ios

works for Swift 3.0 +

self.view.window!.rootViewController?.dismiss(animated: true, completion: nil)

Solution 9 - Ios

I have figured out a generic function to dismiss all presented controllers using the completion block.

extension UIWindow {
    static func keyWindow() -> UIWindow? {
        UIApplication.shared.windows.filter({ $0.isKeyWindow }).first
    }
}

func getVisibleViewController(_ rootViewController: UIViewController?) -> UIViewController? {
    
    var rootVC = rootViewController
    if rootVC == nil {
        rootVC = UIWindow.keyWindow()?.rootViewController
    }
    
    var presented = rootVC?.presentedViewController
    if rootVC?.presentedViewController == nil {
        if let isTab = rootVC?.isKind(of: UITabBarController.self), let isNav = rootVC?.isKind(of: UINavigationController.self) {
            if !isTab && !isNav {
                return rootVC
            }
            presented = rootVC
        } else {
            return rootVC
        }
    }
    
    if let presented = presented {
        if presented.isKind(of: UINavigationController.self) {
            if let navigationController = presented as? UINavigationController {
                return navigationController.viewControllers.last!
            }
        }
        
        if presented.isKind(of: UITabBarController.self) {
            if let tabBarController = presented as? UITabBarController {
                if let navigationController = tabBarController.selectedViewController! as? UINavigationController {
                    return navigationController.viewControllers.last!
                } else {
                    return tabBarController.selectedViewController!
                }
            }
        }
        
        return getVisibleViewController(presented)
    }
    return nil
}

func dismissedAllAlert(completion: (() -> Void)? = nil) {

    if let alert = UIViewController.getVisibleViewController(nil) {

    // If you want to dismiss a specific kind of presented controller then
    // comment upper line and uncomment below one

    // if let alert = UIViewController.getVisibleViewController(nil) as? UIAlertController {

        alert.dismiss(animated: true) {
            self.dismissedAllAlert(completion: completion)
        }

    } else {
        completion?()
    }

}

Note: You call anywhere in code at any class

Use:-

dismissedAllAlert()    // For dismiss all presented controller

dismissedAllAlert {    // For dismiss all presented controller with completion block
    // your code
}

Solution 10 - Ios

Swift Used this to jump directly on your ROOT Navigation controller.

self.navigationController?.popToRootViewController(animated: true)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser2363025View Question on Stackoverflow
Solution 1 - IosLiamView Answer on Stackoverflow
Solution 2 - IosBijender Singh ShekhawatView Answer on Stackoverflow
Solution 3 - IosNaishtaView Answer on Stackoverflow
Solution 4 - IosmarkhorrocksView Answer on Stackoverflow
Solution 5 - IosNitin AlaburView Answer on Stackoverflow
Solution 6 - IoshectorsvillView Answer on Stackoverflow
Solution 7 - IosYun CHENView Answer on Stackoverflow
Solution 8 - IosPardeep KumarView Answer on Stackoverflow
Solution 9 - IosKishan BarmawalaView Answer on Stackoverflow
Solution 10 - IosRahul PanzadeView Answer on Stackoverflow