IOS, how to programmatically determine when a top-level controller is called?

Suppose I have a controller controller stack with two view controllers: VC2 is on top, and VC1 is on the bottom. Is there any code that I can include in VC1 that detects that VC2 has just popped off the stack?

Since I'm trying to detect a VC2 popup from code for VC1, it seems like something like viewWillAppear or viewDidAppear will not work, because these methods every time VC1 is displayed, including when it is first pushed onto the stack.

EDIT: It seems to me that I did not quite understand my original question. Here's what I'm trying to do: determine when VC1 is displayed due to VC2 slipping out of the top of the stack. Here's what I am NOT trying to do: determine when VC1 is displayed due to being inserted at the top of the stack. I need a way that will detect the first action, but NOT the second action.

Note. I don’t really care about VC2, it can be any number of other VCs that exit the stack, which worries me when VC1 again becomes the top of the stack due to the fact that some other VC starts popping up from above.

+48
stack ios uinavigationcontroller
Sep 12
source share
10 answers

IOS 5 introduced two new methods for handling just this type of situation. You are looking for -[UIViewController isMovingToParentViewController] . From docs :

isMovingToParentViewController

Returns a Boolean value that indicates that the view controller is in the process of adding a parent.

- (BOOL)isMovingToParentViewController

Return value
YES if the view controller appears because it was added as a child of the container, otherwise NO.

Discussion
This method returns YES only when calling the following methods from within:

-viewWillAppear:
-viewDidAppear:

In your case, you can implement -viewWillAppear: as follows:

 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; if (self.isMovingToParentViewController == NO) { // we're already on the navigation stack // another controller must have been popped off } } 

EDIT: There's a subtle semantic difference to consider here - are you interested in the fact that VC2, in particular, popped off the stack, or do you want to receive notifications every time VC1 is displayed as the result of a controller? In the first case, delegation is the best solution. Direct weak link to VC1 may also work if you never intend to reuse VC2.

EDIT 2: I made the example more explicit by inverting the logic and not returning earlier.

+61
Sep 12 '12 at 4:20
source share

isMovingTo / FromParentViewController will not work to push and push the navigation controller onto the stack.

Here's a reliable way to do this (without using a delegate), but this is probably only iOS 7+.

 UIViewController *fromViewController = [[[self navigationController] transitionCoordinator] viewControllerForKey:UITransitionContextFromViewControllerKey]; if ([[self.navigationController viewControllers] containsObject:fromViewController]) { //we're being pushed onto the nav controller stack. Make sure to fetch data. } else { //Something is being popped and we are being revealed } 

In my case, using a delegate would mean that the behavior of the view manager is more closely related to the delegate who owns the nav stack, and I would like a more autonomous solution. It works.

+11
Oct 10 '14 at
source share

One way you could approach is to declare a delegate protocol for VC2 something like this:

in vc1.h

 @interface VC1 : UIViewController <VC2Delegate> { ... } 

in VC1.m

 -(void)showVC2 { VC2 *vc2 = [[VC2 alloc] init]; vc2.delegate = self; [self.navigationController pushViewController:vc2 animated:YES]; } -(void)VC2DidPop { // Do whatever in response to VC2 being popped off the nav controller } 

in vc2.h

 @protocol VC2Delegate <NSObject> -(void)VC2DidPop; @end @interface VC2 : UIViewController { id<VC2Delegate> delegate; } @property (nonatomic, assign) id delegate; ... @end 

VC2.m

 -(void)viewDidUnload { [super viewDidUnload]; [self.delegate VC2DidPop]; } 

There is a good article on the basics of protocols and delegates here .

+7
Sep 12
source share

You can also find in the view controller that is being called

 - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; if ([self isMovingFromParentViewController]) { .... } } 
+3
Sep 06 '16 at 6:59
source share

It worked for me

 UIViewController *fromViewController = [[[self navigationController] transitionCoordinator] viewControllerForKey:UITransitionContextFromViewControllerKey]; if (![[self.navigationController viewControllers] containsObject:fromViewController] && !self.presentedViewController) { //Something is being popped and we are being revealed } 
+1
May 25 '17 at 11:56 a.m.
source share

What are you specifically trying to do?

If you are trying to discover that VC1 will be shown, this answer should help you. Use UINavigationControllerDelegate .

If you are trying to discover that VC2 will be hidden, I just use viewWillDisappear: VC2.

0
Sep 12
source share

You can add an observer for NSNotification specifically JUST for your VC2.

 // pasing the "VC2" here will tell the notification to only listen for notification from // VC2 rather than every single other objects [[NSNotitificationCenter defaultCenter] addObserver:self selector:@selector(doSomething:) object:VC2]; 

Now in your view VC2 will disappear, you can send a notification:

 -(void)viewWillDisappear { [[NSNotificationCenter defaultCenter] postNotificationNamed:@"notif_dismissingVC2" object:nil]; } 
0
Sep 12
source share

Yes, in VC1 you can check if VC2 pops up. In the UINavigationController, there is one viewControllers method that returns an array of pushed controllers that are on the stack (i.e., that were pushed).

So you repeat the loop by comparing the class. If VC2 will match, otherwise not.

0
Sep 12
source share

swift 3

 override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) if self.isMovingToParentViewController { print("View is moving to ParentViewControll") } } 
0
May 11 '17 at 8:59
source share

I have the same situation, but with a small specific use case. In my case, we wanted to determine if VC1 / appears when the user presses the VC2 button back, where VC2 clicks on the navigationController via VC1.

So, I used snarshad's help answer to configure as per my need. Here is the code in VC1 viewDidAppear in swift 3 .

 // VC1: ParentViewController // VC2: ChildViewController override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) if let transitionCoordinator = navigationController?.transitionCoordinator, let fromVC = transitionCoordinator.viewController(forKey: UITransitionContextViewControllerKey.from), let toVC = transitionCoordinator.viewController(forKey: UITransitionContextViewControllerKey.to), fromVC is ChildViewController, toVC is ParentViewController { print("Back button pressed on ChildViewController, and as a result ParentViewController appeared") } } 
0
Sep 08 '17 at 11:02 on
source share



All Articles