How to identify previous view controller in navigation stack

Objective CIosUiviewcontrollerUinavigationcontroller

Objective C Problem Overview


I have 2 seperate navigationcontrollers, one with RootViewController A and the other with RootViewController B.

I am able to push ViewController C onto either A or B's navigation stack.

Question: When I am in ViewController C, how can I find out if I am in the stack belonging to A or B?

Objective C Solutions


Solution 1 - Objective C

You could use the UINavigationController's viewControllers property:

> @property(nonatomic, copy) NSArray *viewControllers > > Discussion: The root view controller is at index 0 in the array, the back view controller is at index n-2, and the top controller is at index n-1, where n is the number of items in the array.

https://developer.apple.com/documentation/uikit/uinavigationcontroller

You could use that to test whether the root view controller (the one at array index 0) is view controller A or B.

Solution 2 - Objective C

Here's the implementation of the accepted answer:

- (UIViewController *)backViewController
{
    NSInteger numberOfViewControllers = self.navigationController.viewControllers.count;
    
    if (numberOfViewControllers < 2)
        return nil;
    else
        return [self.navigationController.viewControllers objectAtIndex:numberOfViewControllers - 2];
}

Solution 3 - Objective C

- (UIViewController *)backViewController
{
    NSInteger myIndex = [self.navigationController.viewControllers indexOfObject:self];

    if ( myIndex != 0 && myIndex != NSNotFound ) {
        return [self.navigationController.viewControllers objectAtIndex:myIndex-1];
    } else {
        return nil;
    }
}

Solution 4 - Objective C

A more general implementation of the accepted answer:

- (UIViewController *)backViewController {
    NSArray * stack = self.navigationController.viewControllers;

    for (int i=stack.count-1; i > 0; --i)
        if (stack[i] == self)
            return stack[i-1];

    return nil;
}

This will return the correct "back view controller" regardless of where the current class is on the navigation stack.

Solution 5 - Objective C

Swift implementation of tjklemz code as an extension:

extension UIViewController {

    func backViewController() -> UIViewController? {        
        if let stack = self.navigationController?.viewControllers {
            for(var i=stack.count-1;i>0;--i) {
                if(stack[i] == self) {
                    return stack[i-1]
                }
            }
        }
        return nil
    }
}

Solution 6 - Objective C

Here's a modifed version of Prabhu Beeman's Swift code that adapts it to support Swift 3:

extension UIViewController {
    func backViewController() -> UIViewController? {
        if let stack = self.navigationController?.viewControllers {
            for i in (1..<stack.count).reverse() {
                if(stack[i] == self) {
                    return stack[i-1]
                }
            }
        }
        return nil
    }
}

Solution 7 - Objective C

As a UINavigationController extension:

extension UINavigationController {

    func previousViewController() -> UIViewController? {
        guard viewControllers.count > 1 else {
            return nil
        }
        return viewControllers[viewControllers.count - 2]
    }

}

Solution 8 - Objective C

Access the n-2 element of the viewControllers property to access the parent view controller.

Once you have that instance, you can check its type by logging what comes out of the NSStringFromClass() function. Or you could keep some static const identifier string in controllers A and B, and a getter function that prints out the string.

Solution 9 - Objective C

Swift implementation of @tjklemz code:

var backViewController : UIViewController? {
            
        var stack = self.navigationController!.viewControllers as Array
            
        for (var i = stack.count-1 ; i > 0; --i) {
            if (stack[i] as UIViewController == self) {
                return stack[i-1] as? UIViewController
            }
                
        }
        return nil
    }

Solution 10 - Objective C

Use the navigationController method to retrieve it. See documentation on Apple's site.

> navigationController A parent or ancestor that is a navigation > controller. (read-only) > > @property(nonatomic, readonly, retain) UINavigationController > *navigationController > > Discussion Only returns a navigation controller if the view > controller is in its stack. This property is nil if a navigation > controller cannot be found. > > Availability Available in iOS 2.0 and later.

Solution 11 - Objective C

Find the previous view controller is simple.

In your case, i.e., you are in C and you need B, [self.navigationController.viewControllers lastObject] is what you want.

For A, since A is the root view controller, you can just replace lastObject with firstObject to obtain.

Solution 12 - Objective C

Because dead horses enjoy being beaten :)

- (UIViewController *)previousViewController
{
    NSArray *vcStack = self.navigationController.viewControllers;
    NSInteger selfIdx = [vcStack indexOfObject:self];
    if (vcStack.count < 2 || selfIdx == NSNotFound) { return nil; }
    return (UIViewController *)[vcStack objectAtIndex:selfIdx - 1];
}

Solution 13 - Objective C

A more succinct Swift impementation:

extension UIViewController {
    var previousViewController: UIViewController? {
        guard let nav = self.navigationController,
              let myIdx = nav.viewControllers.index(of: self) else {
            return nil
        }
        return myIdx == 0 ? nil : nav.viewControllers[myIdx-1]
    }
}

Solution 14 - Objective C

Implementation for Swift 2.2 - Add this in an UIViewController extension. Safe in the sense it will return nil if the viewcontroller is the rootvc or not in a navigationcontroller.

var previousViewController: UIViewController? {
  guard let viewControllers = navigationController?.viewControllers else {
    return nil
  }
  var previous: UIViewController?
  for vc in viewControllers{
    if vc == self {
      break
    }
    previous = vc
  }
  return previous
}

Solution 15 - Objective C

With Swift using guard.

extension UIViewController {
    
    func getPreviousViewController() -> UIViewController? {
        guard let _ = self.navigationController else {
            return nil
        }
        
        guard let viewControllers = self.navigationController?.viewControllers else {
            return nil
        }
        
        guard viewControllers.count >= 2 else {
            return nil
        }        
        return viewControllers[viewControllers.count - 2]
    }
}

Solution 16 - Objective C

My extension, swift 3

extension UIViewController {
    var previousViewController: UIViewController? {
        guard let controllers = navigationController?.viewControllers, controllers.count > 1 else { return nil }
        switch controllers.count {
        case 2: return controllers.first
        default: return controllers.dropLast(2).first
        }
    }
}

Solution 17 - Objective C

for swift 3 you can do this:

var backtoViewController:UIViewController!
for viewController in (self.navigationController?.viewControllers)!.reversed() {
    if viewController is NameOfMyDestinationViewController {
            backtoViewController = viewController
    }
}
self.navigationController!.popToViewController(backtoViewController, animated: true)

You only need replace "NameOfMyDestinationViewController" by viewController that you want to return.

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
QuestionZhenView Question on Stackoverflow
Solution 1 - Objective Cjburns20View Answer on Stackoverflow
Solution 2 - Objective CGeorgeView Answer on Stackoverflow
Solution 3 - Objective CChrisView Answer on Stackoverflow
Solution 4 - Objective CtjklemzView Answer on Stackoverflow
Solution 5 - Objective CgrassyburritoView Answer on Stackoverflow
Solution 6 - Objective CCin316View Answer on Stackoverflow
Solution 7 - Objective CAdam JohnsView Answer on Stackoverflow
Solution 8 - Objective CAlex ReynoldsView Answer on Stackoverflow
Solution 9 - Objective Ccdf1982View Answer on Stackoverflow
Solution 10 - Objective CPerceptionView Answer on Stackoverflow
Solution 11 - Objective Cchih-chun chanView Answer on Stackoverflow
Solution 12 - Objective CAndrewView Answer on Stackoverflow
Solution 13 - Objective CJohn ScaloView Answer on Stackoverflow
Solution 14 - Objective CBlake LockleyView Answer on Stackoverflow
Solution 15 - Objective CbaquiaxView Answer on Stackoverflow
Solution 16 - Objective CDmytro NasyrovView Answer on Stackoverflow
Solution 17 - Objective Cgandhi MenaView Answer on Stackoverflow