54

I am using navigation based application. I push First ViewController to Second ViewController and from Second ViewController to Third ViewController. Now I want to pop from Third ViewController to First ViewController.I am performing this task using the below code but my application crashed.

Please any body give me some proper guidelines. I can't use pop to rootViewController because it's different viewController. Thanks in advance...

In Third ViewControler i have written this:

FirstViewCtr *x=[[FirstViewCtr alloc] initWithNibName:@"FirstViewCtr" bundle:nil];
[self.navigationController popToViewController:x animated:NO];
Sushil Sharma
  • 2,231
  • 3
  • 23
  • 48
Ankit Vyas
  • 7,401
  • 13
  • 52
  • 87

16 Answers16

95

By Writing the First Line you get the Indexes of all View Controllers and from second Line You will reach up to your Destination.

NSArray *array = [self.navigationController viewControllers];

[self.navigationController popToViewController:[array objectAtIndex:2] animated:YES];
Hiren
  • 12,525
  • 7
  • 51
  • 71
Ankit Vyas
  • 7,401
  • 13
  • 52
  • 87
  • 1
    thats amazing. +1 .. brilliant. im using this in my app right now. – Pavan Aug 23 '12 at 23:52
  • 4
    Or, for the one liner cheat people: [self.navigationController popToViewController:[[self.navigationController viewControllers]objectAtIndex:2] animated:YES]; – Bob Arezina Oct 09 '14 at 05:36
79

A safer approach:

- (void)turnBackToAnOldViewController{

    for (UIViewController *controller in self.navigationController.viewControllers) {

        //Do not forget to import AnOldViewController.h
        if ([controller isKindOfClass:[AnOldViewController class]]) { 

            [self.navigationController popToViewController:controller
                                                  animated:YES];
            return;
        }
    }
}
iHarshil
  • 611
  • 8
  • 19
Yunus Nedim Mehel
  • 11,371
  • 4
  • 47
  • 54
  • Safer than a native method? How so? – JohnK Jun 01 '13 at 01:33
  • 6
    The native method is the message popToViewController:animated, not the way you are providing the parameters to the message. I believe this solution is safer because when you say [array objectAtIndex:2] , you automatically assume that the number of VC's is bigger than 2. If thats not the case, the program will crash, with my solution it will stand still and wont crash if there is no instance of the required VC. – Yunus Nedim Mehel Jun 03 '13 at 09:17
  • i think that your aproach its really better than the accepted solution @YunusNedimMehel. Whatever, and totally offtopic, i think break sentence is not needed there –  May 06 '14 at 08:56
  • @oPi It is actually relevant, lets discuss that. I have not tested it, but if the for loop does not terminate after popping, it might cause a crash. What if there is another instance of AnOldViewController on the stack? – Yunus Nedim Mehel May 06 '14 at 09:12
  • @YunusNedimMehel ive not tested it too, but i think that, when popviewcontroller is called, the current view controller is deallocated (and i think that main thread operations should be stopped) –  May 06 '14 at 09:45
  • 1
    @oPi It is still risky though, because the top viewController will be deallocated after popping animation is completed (0.3s I guess?). So it's better to avoid that – Yunus Nedim Mehel May 06 '14 at 10:37
  • Yeah, this is the better solution then the accepted answer... it is dynamic where in the accepted answer contains static code... – Kanan Vora Jan 13 '15 at 11:44
  • let suppose AnOldViewController is not there in navigation stack even you want to pop into AnOldViewController. How you will implement this? – Gajendra Rawat Feb 10 '16 at 10:06
  • 2
    @morroko If it's not on the stack, you cannot pop into it. You can either push it, or manually insert it inside the viewControllers stack (it's a public, readwrite NSArray property) – Yunus Nedim Mehel Feb 10 '16 at 12:23
19

Swifty way:

     let dashboardVC = navigationController!.viewControllers.filter { $0 is YourViewController }.first!
     navigationController!.popToViewController(dashboardVC, animated: true)
SwiftGod
  • 376
  • 2
  • 5
7
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];
Rajesh
  • 9,724
  • 14
  • 37
  • 58
Vikash Kumar
  • 1,224
  • 1
  • 10
  • 23
7

Swift 4 version

if let viewController = navigationController?.viewControllers.first(where: {$0 is YourViewController}) {
      navigationController?.popToViewController(viewController, animated: false)
}

You may specify another filter on .viewControllers.first as per your need e.g lets say if you have same kind of view controllers residing in the navigation controller then you may specify an additional check like below

  if let viewController = navigationController?.viewControllers.first(where: {
        if let current = $0 as? YourViewController {
             return current.someProperty == "SOME VALUE"
        }
       return false } ) {
            navigationController?.popToViewController(viewController, animated: false)
   }
Umair
  • 956
  • 9
  • 11
6

Often it is more important to do that from top of stack, so:

- (void)popToLast:(Class)aClass
{
    for (int i=self.navigationController.viewControllers.count-1; i>=0; i--)
    {
        UIViewController *vc = self.navigationController.viewControllers[i];
        if ([vc isKindOfClass:aClass])
        {
            [self.navigationController popToViewController:vc animated:YES];
            break;
        }
    }
}

and you call that

popToLast:[SomeViewController class];
Leszek Zarna
  • 2,973
  • 24
  • 25
6
- (void) RetunToSpecificViewController{

   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;
            }
    }
}
Dilip
  • 8,873
  • 4
  • 40
  • 56
Mahendra Y
  • 1,669
  • 17
  • 25
2

Quick and safe Swift 3 version:

if let vc = navigationController.viewControllers.filter({ $0 is SpecificViewControllerClass }).first {
  navigationController.popToViewController(vc, animated: true)
}
budiDino
  • 10,932
  • 8
  • 84
  • 83
1

Your code creates a new instance of a view that has never been pushed onto the stack, then tries to pop back to that controller.

If you are popping back to the root view controller, you can uses popToRootViewControllerAnimated:

If you are popping back a known distance you can call popViewControllerAnimated: more than once. In your example, that would be 2 controllers so to calls. You could do the same thing by looking in viewControllers for the controller 2 from the end and popping to it.

The above suggestions are quick fixes. One best practice scenario would be to pass the controller you want to return to along to each successive controller you push. First passes itself to second, second passes that reference to third, third pops to the passed reference, which is first.

In effect, you are creating a temporary root controller. You could subclass UINavigationController and add a temporaryRoot property and a popToTemporaryRootViewControllerAnimated: method that would pop to your temporary root and clear it. When first pushes seconds, it would also set itself as the temporary root so that every controller in the stack does not have to pass a reference around. You would have to add some extra checks to unsure you never pop past the temporaryRoot without clearing it.

drawnonward
  • 52,256
  • 15
  • 103
  • 110
  • I have RootViewController then it Pushed into FirstViewController then after SecondViewController then after ThirdViewController while i reach upto ThirdViewController i required pop Directly to the FirstViewController. – Ankit Vyas Jun 12 '10 at 08:06
1

After lots of effort someone has created swift extension of back to a particular view controller in Swift 3.0.

extension UINavigationController {

    func backToViewController(viewController: Swift.AnyClass) {

            for element in viewControllers as Array {
                if element.isKind(of: viewController) {
                    self.popToViewController(element, animated: true)
                break
            }
        }
    }
}

Method calling:

 self.navigationController?.backToViewController(viewController: YourViewController.self)
Ved Rauniyar
  • 1,398
  • 10
  • 21
1

Implemented & Tested in Swift 3.0

Below is Method which can useful for Navigate to any specific View Controller :

func poptoSpecificVC(viewController : Swift.AnyClass){
        let viewControllers: [UIViewController] = self.navigationController!.viewControllers
        for aViewController in viewControllers {
            if aViewController.isKind(of: viewController) {
                self.navigationController!.popToViewController(aViewController, animated: true)
                break;
            }
        }
    }

Usage :

self.poptoSpecificVC(viewController: createIntervalVC.self)
Ketan P
  • 3,899
  • 3
  • 28
  • 34
1

I think that .filter({...}).first is a little bit slower than .first(where: {...}). Also this could be written more precisely to address only UIViewControllers.

extension UINavigationController {
    func popToController<T: UIViewController>(_ type: T.Type, animated: Bool) {
        if let vc = viewControllers.first(where: { $0 is T }) {
            popToViewController(vc, animated: animated)
        }
    }
    func popToControllerOrToRootControllerIfNotInTheStack<T: UIViewController>(_ type: T.Type, animated: Bool) {
        if let vc = viewControllers.first(where: { $0 is T }) {
            popToViewController(vc, animated: animated)
        } else {
            popToRootViewController(animated: animated)
        }
    }
}
1

Updated for Swift 3:

used below simple code, for pop to specific view controller;

 for vc in self.navigationController!.viewControllers as Array {
          if vc.isKind(of: YourViewControllerName) {
               self.navigationController!.popToViewController(vc, animated: true)
               break
          }
    }
Kiran Jadhav
  • 2,729
  • 22
  • 26
0
for controller in self.navigationController!.viewControllers as Array {
        if controller.isKind(of: LoginVC.self) {
            _ =  self.navigationController!.popToViewController(controller, animated: true)
            break
        }
    }
Ankit Vyas
  • 7,401
  • 13
  • 52
  • 87
0

Put function in UIViewController 1. it checks if Specific UIViewController exists In UINavigationController then popToViewController or else pushViewController

func navigate(_ navVC: AnyClass, pushVC: UIViewController) {
    for obj in self.navigationController!.viewControllers {
        if obj.isMember(of: navVC) {
            self.navigationController!.popToViewController(obj, animated: true)
            return
        }
    }
    self.navigationController!.pushViewController(pushVC, animated: true)
}

Use

self.navigate(ViewController.self, pushVC: self.storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController)
Harsh Prajapati
  • 1,281
  • 1
  • 11
  • 23
0

i have answer here. This is 100% working code for Swift > 4.X

How can I pop specific View Controller in Swift

Ashu
  • 2,867
  • 29
  • 30