64

I used the Objective-C code below to pop a specific ViewController.

for (UIViewController *controller in self.navigationController.viewControllers) {
    if ([controller isKindOfClass:[AnOldViewController class]]) { 
        //Do not forget to import AnOldViewController.h
        [self.navigationController popToViewController:controller
                                              animated:YES];
        break;
    }
}

How can I do that in Swift?

ZGski
  • 2,398
  • 1
  • 21
  • 34
Mahesh Cheliya
  • 1,334
  • 1
  • 16
  • 21
  • 14
    Exactly the same way, but in Swift syntax. What have you tried, and what goes wrong when you try it? (Specific error message or runtime behavior that you're not expecting). – Duncan C May 02 '15 at 15:08

19 Answers19

133

Try following code:

for controller in self.navigationController!.viewControllers as Array {
    if controller.isKind(of: ViewController.self) {
        self.navigationController!.popToViewController(controller, animated: true)
        break
    }
}
Zigii Wong
  • 7,766
  • 8
  • 51
  • 79
Mohit
  • 3,708
  • 2
  • 27
  • 30
45

Swift 5

To pop to the latest instance of a specific class, for example SomeViewController:

navigationController?.popToViewController(ofClass: SomeViewController.self)

But you need to add ths UINavigationController extension:

extension UINavigationController {
  func popToViewController(ofClass: AnyClass, animated: Bool = true) {
    if let vc = viewControllers.last(where: { $0.isKind(of: ofClass) }) {
      popToViewController(vc, animated: animated)
    }
  }
}
budiDino
  • 13,044
  • 8
  • 95
  • 91
36

For Swift 3+

let viewControllers: [UIViewController] = self.navigationController!.viewControllers
for aViewController in viewControllers {
    if aViewController is YourViewController {
        self.navigationController!.popToViewController(aViewController, animated: true)
    }
}
Museer Ahamad Ansari
  • 5,414
  • 3
  • 39
  • 45
26

From Swift 4.0 and Above

for controller in self.navigationController!.viewControllers as Array {
        if controller.isKind(of: DashboardVC.self) {
            _ =  self.navigationController!.popToViewController(controller, animated: true)
            break
        }
    }

This is working Perfect.

Ashu
  • 3,373
  • 38
  • 34
18

I prefer a generic way to do it.

I have this extension for the UINavigationController :

extension UINavigationController {

   func backToViewController(vc: Any) {
      // iterate to find the type of vc
      for element in viewControllers as Array {
         if "\(element.dynamicType).Type" == "\(vc.dynamicType)" {
            self.popToViewController(element, animated: true)
            break
         }
      }
   }

}

Let's say I have a FOHomeVC class (who is a UIViewController) instantiated in the navigation stack.

So I would do this in my code:

self.navigationController?.backToViewController(FOHomeVC.self)
PabloR
  • 506
  • 7
  • 7
6

I have added an extension to UINavigationController which helps you to find if that controller exist in navigation stack. If yes then it will be popped to that controller or else you pass new controller to push with pushController param.

extension UINavigationController {

    func containsViewController(ofKind kind: AnyClass) -> Bool {
        return self.viewControllers.contains(where: { $0.isKind(of: kind) })
    }

    func popPushToVC(ofKind kind: AnyClass, pushController: UIViewController) {
        if containsViewController(ofKind: kind) {
            for controller in self.viewControllers {
                if controller.isKind(of: kind) {
                    popToViewController(controller, animated: true)
                    break
                }
            }
        } else {
            pushViewController(pushController, animated: true)
        }
    }
}
Parth Adroja
  • 13,198
  • 5
  • 37
  • 71
6

Swift 4 / Swift 5

 for controller in self.navigationController!.viewControllers as Array {
            if controller.isKind(of: HomeViewController.self) {
                self.navigationController!.popToViewController(controller, animated: true)
                break
            }
        }
Pranit
  • 892
  • 12
  • 20
5

I prefer a "real generic" and more functional approach.

So I came up with following UINavigationController extension functions. You can also use the first function, for anything else, where you just need to access a specific VC in the navigation stack.


Extensions

extension UINavigationController {
    func getViewController<T: UIViewController>(of type: T.Type) -> UIViewController? {
        return self.viewControllers.first(where: { $0 is T })
    }

    func popToViewController<T: UIViewController>(of type: T.Type, animated: Bool) {
        guard let viewController = self.getViewController(of: type) else { return }
        self.popToViewController(viewController, animated: animated)
    }
}

Usage

self.navigationController?.popToViewController(of: YourViewController.self, animated: true)



This should work at least in Swift 4 and 5.

d4Rk
  • 6,622
  • 5
  • 46
  • 60
4

Find your view controller from navigation stack and pop to that view controller if it exists

for vc in self.navigationController!.viewControllers {
    if let myViewCont = vc as? VCName 
    {
        self.navigationController?.popToViewController(myViewCont, animated: true)
    }
}
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
Chinju James
  • 201
  • 3
  • 8
4

swift5

let controllers : Array = self.navigationController!.viewControllers
self.navigationController!.popToViewController(controllers[1], animated: true)
Mohsen Fard
  • 597
  • 9
  • 22
3

Swift 5 Answer of @PabloR is Here :

extension UINavigationController {

   func backToViewController(vc: Any) {
      // iterate to find the type of vc
      for element in viewControllers as Array {
        if "\(type(of: element)).Type" == "\(type(of: vc))" {
            self.popToViewController(element, animated: true)
            break
         }
      }
   }

}

Usage :

self.navigationController?.backToViewController(vc: TaskListViewController.self)
2

In latest swift

   @IBAction func popToConversationsVC(_ sender: UIButton) {
        if (self.navigationController != nil) {
            for vc in  self.navigationController!.viewControllers {
                if vc is ConversationsVC {
                     self.navigationController?.popToViewController(vc, animated: false)
                }
            }
            }
    }
2

For Swift 4.0 and above Using Filter

guard let VC = self.navigationController?.viewControllers.filter({$0.isKind(of: YourViewController.self)}).first else {return}
self.navigationController?.popToViewController(VC, animated: true)

OR

if let vc = self.navigationController?.viewControllers.map({$0 as? YourViewController}).first{
            self.navigationController?.popToViewController(vc!, animated: true)
        }
Mehsam Saeed
  • 235
  • 2
  • 14
1

Please use this below code for Swift 3.0:

 let viewControllers: [UIViewController] = self.navigationController!.viewControllers as [UIViewController];

for aViewController:UIViewController in viewControllers {
            if aViewController.isKind(of: YourViewController.self) {
                _ = self.navigationController?.popToViewController(aViewController, animated: true)
            }
        }
urvashi bhagat
  • 1,123
  • 12
  • 15
1

I needed to use this, because in some cases app crashes:

if let navVC = self.navigationController {
                    let views = navVC.viewControllers as Array
                    for controller in views {
                        if controller.isKind(of: YourVC.self) {
                            navVC.popToViewController(controller, animated: true)
                            return
                        }
                    }
                }
clopex
  • 459
  • 6
  • 11
1

This solution worked for me :)

extension UINavigationController {
  func backToViewController(_ viewController: AnyClass, animated: Bool) {

    guard let viewController = self.viewControllers.first(where: {$0.isKind(of: viewController)}) else { return }
    self.popToViewController(viewController, animated: animated)
  }
}
Yakup Ad
  • 1,591
  • 16
  • 13
1

I adapt from all answer above. It look like Yakup Ad answer, because it's very short way.

  • I force type by using generic for argument, that make sure you must pass only UIViewController to this func.
  • I search viewController that already in stack by using .first this make me got only one VC then stop the loop.
  • I also return passing VC back if you need to customize somethings.

Let's enjoy.

extension UINavigationController {

    func popToViewController<T: UIViewController>(_ viewController: T.Type, animated: Bool) -> T? {
        guard let viewController = self.viewControllers.first(where: {$0 is T}) else { return nil }
        self.popToViewController(viewController, animated: animated)
        
        return viewController as? T
    }
    
}

Usage

let poppedVC = self.navigationController?.popToViewController(HomeViewController.self, animated: true)
LordGift
  • 51
  • 1
  • 7
0
extension UINavigationController {
    func popBack(to vc: AnyClass, animated: Bool = true) {
        guard let elementFound = (viewControllers.filter { $0.isKind(of: vc) }).first else {
            fatalError("cannot pop back to \(vc) as it is not in the view hierarchy")
        }
        self.popToViewController(elementFound, animated: animated)
    }
}
0

simple and best solution without force unwrapped is

if let vc = navigationController.viewControllers.filter({$0 is YourViewController}).first as? YourViewController {
            self.navigationController.popToViewController(vc, animated: true)
            }
Ali Subhani
  • 169
  • 1
  • 7