64

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?

Eric Aya
  • 68,765
  • 33
  • 165
  • 232
user2363025
  • 5,389
  • 15
  • 41
  • 86
  • You might just want to reinitialize your app's `UIWindow`'s `rootViewController` property. That will remove your entire view hierarchy (assuming you're not using other windows, which is rare). – Aaron Brager Nov 04 '15 at 11:44
  • Are you presenting controllers or just pushing onto the nav stack? Where is the logout button? Can you just pop to root? What other clean up do you need to do? – Wain Nov 04 '15 at 11:44
  • @Wain I have my controller linked up with segue identifiers in certain cases and I am presentingViewControllers with the defined identifier. Then there are some cases where I'm just doing basic segues i.e. a button's action, a table row's click event. The logout button is added to the nav bar as a left bar button item. No other clean up really. Reset a few Global Prefs just – user2363025 Nov 04 '15 at 11:51
  • @Aaron Brager sorry if this is a silly question, but what do you mean exactly about using other windows? – user2363025 Nov 04 '15 at 11:52
  • @user2363025 Very rarely an iOS app will have multiple UIWindows. You would know if you were making and managing them. – Aaron Brager Nov 04 '15 at 12:07

9 Answers9

194

You can call :

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

Should dismiss all view controllers above the root view controller.

Krunal Nagvadia
  • 820
  • 1
  • 6
  • 23
Liam
  • 2,369
  • 1
  • 12
  • 16
  • Give this a go, let me know how it goes :), also assuming that self is the view controller that you are calling logout from. If it is called from somewhere else, you would need to pass the controller and replace self with the controller you would be calling logout from. – Liam Nov 04 '15 at 12:09
  • @Zorayr just change `false` to `true` for dismissAnimation, it should work: `self.view.window!.rootViewController?.dismissViewControllerAnimated(true, completion: nil)` – oyalhi Apr 19 '17 at 09:39
  • @Liam `self.view.window!.rootViewController?.dismissViewControllerAnimated(false, completion: nil) let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil) let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewControllerWithIdentifier("LogIn") as UIViewController self.appDelegate.window = UIWindow(frame: UIScreen.mainScreen().bounds) self.appDelegate.window?.rootViewController = initialViewControlleripad self.appDelegate.window?.makeKeyAndVisible()` This is my code – Coder Aug 03 '17 at 11:52
  • 1
    Suppose tested my app 5 times in that 3 times 'deinit {}' method is called sometime not. What is wrong with this ? And background my 'NSTimer' logic is working – Coder Aug 03 '17 at 11:55
  • 3
    Swift 3.0 syntax is `self.view.window!.rootViewController?.dismiss(animated: false, completion: nil)` – Pinwheeler Aug 07 '18 at 17:53
39

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)
Bijender Singh Shekhawat
  • 2,707
  • 1
  • 21
  • 31
30

Swift 4:

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.

Naishta
  • 9,905
  • 4
  • 60
  • 49
7

Swift3

navigationController?.popToRootViewControllerAnimated(true)
markhorrocks
  • 1,206
  • 12
  • 59
  • 113
6

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

Community
  • 1
  • 1
Nitin Alabur
  • 5,654
  • 1
  • 30
  • 51
4

To dismiss all modal Views.

Swift 5


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

hectorsvill
  • 109
  • 1
  • 6
0

works for Swift 3.0 +

self.view.window!.rootViewController?.dismiss(animated: true, completion: nil)
Vega
  • 23,736
  • 20
  • 78
  • 88
0

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)

        //......
    }

}
Yun CHEN
  • 5,785
  • 3
  • 24
  • 31
-1

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

self.navigationController?.popToRootViewController(animated: true)

Rahul Panzade
  • 902
  • 10
  • 9