79

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?

2
  • 7
    Why not just create a previousViewController property on C that you set when you push C? Or give C the info it need from A or B when you push it? Commented Aug 10, 2011 at 4:25
  • 1
    @TerryWilcox , 'cos the relations could be very complicated nowadays Commented Oct 24, 2016 at 13:54

17 Answers 17

107

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.

Sign up to request clarification or add additional context in comments.

4 Comments

How can I get this as a string in SWIFT.. (I'm new to swift)
@cyberboy just as easy: with var viewControllers: [UIViewController]
Previous view controller will be at viewControllers.last
@Burak viewControllers.last has current view controller, In order to get previous view controller one should use viewControllers[viewControllers.count-2]
105

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]; } 

1 Comment

I recommend adding this as a category on UIViewController
29
- (UIViewController *)backViewController { NSInteger myIndex = [self.navigationController.viewControllers indexOfObject:self]; if ( myIndex != 0 && myIndex != NSNotFound ) { return [self.navigationController.viewControllers objectAtIndex:myIndex-1]; } else { return nil; } } 

Comments

12

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.

Comments

9

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

1 Comment

I've posted a modified version of this code that has Swift 3 support below as a new answer.
9

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

1 Comment

a couple of notes: reverse() now is reversed() and the self.navigationController?.viewControllers never contains the "self" view controller, so return stack.last should be enough
6

As a UINavigationController extension:

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

1 Comment

The only answer who extended correctly UINavigationController instead of UIViewController
3

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.

1 Comment

any simple way of getting the current index of a view controller or shall I hardcode them depending on my hierarchy structure.
2

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 } 

2 Comments

How can I get this as a string.. (I'm new to swift)
@cyberboy Hi! I'm not sure I understand your question. Do you want to get the backViewController description as a String? If so, you might consider to println("backViewController is: \(backViewController.description)"); this will give you a description like "UINavigationController: 0x7fcea1d286b0", which is not very clear but at least allows you to identify the object. As I said, I'm not sure of what you need exactly...
1

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.

Comments

1

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.

Comments

1

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]; } 

Comments

1

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

Comments

0

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 } 

Comments

0

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

Comments

0

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

Comments

0

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.