单个函数来解除所有打开的视图控制器

我有一个应用程序,这是一个单视图应用程序。我有一个从根视图控制器链接到所有子控制器的导航控制器。

在每个子控制器中,都有一个注销按钮。我想知道如果我可以有一个单一的功能,我可以调用它将解除所有的控制器已经打开了一路上,无论哪个控制器当前打开时,用户按注销?

我的基本起点:

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)
}

我正在寻找执行这项任务的最有效的记忆方法。我将把我的注销函数放在一个单独的 utils 文件中,但是我不能使用 self。我还有一个问题,就是要知道动态地解雇哪些控制器。

更新 建议使用流行到 root 视图控制器,所以我的尝试是这样的:

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

这是达到我目的的最好方法吗?

97745 次浏览

You can call :

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

Should dismiss all view controllers above the root view controller.

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

Swift3

navigationController?.popToRootViewControllerAnimated(true)

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)

works for Swift 3.0 +

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

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.

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

self.navigationController?.popToRootViewController(animated: true)

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)


//......
}


}

To dismiss all modal Views.

Swift 5


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


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
}

If you have access to Navigation Controller, you can try something like this. Other solutions didn't work for me.

func popAndDismissAllControllers(animated: Bool) {
var presentedController = navigationController?.presentedViewController


while presentedController != nil {
presentedController?.dismiss(animated: animated)
presentedController = presentedController?.presentedViewController
}


navigationController?.popToRootViewController(animated: animated)
}