views:

693

answers:

2

I'm switching views in the context of a navigation view heirarchy, and I want to be able to determine at switch time what the prior view was that is being pushed under the new view.

I'm trying this in a UINavigationControllerDelegate:

(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    NSLog( @"Switching from %@ to %@", 
     NSStringFromClass( [[navigationController visibleViewController] class] ),
     NSStringFromClass( [viewController class] )
    );
}

I get this:

2009-08-05 20:05:21.274 App Name [85913:20b] Switching from ManagementScreen to ManagementScreen

unfortunately it appears that before "will" is called, it is already swapped out in the state of the UINavigationController such that viewController passed in is always the same as the visibleViewController on the UINavigationController (and also the topViewController property, not demonstrated here but I tried it with the same code).

I would like to avoid extending the navigation view controller, and honestly while I can easily put a property on the delegate - however I'm wondering if this behavior is possible within the existing framework (seems will should be called before it happens where as did happens after, but it seems the state of the navigation controller is modified before either).

Thanks!

+1  A: 
- (void)navigationController:(UINavigationController*)nc
       didShowViewController:(UIViewController*)vc
                    animated:(BOOL)animated
{
  NSLog(@"Switching from %@ to %@",
   NSStringFromClass([vc class]),
   NSStringFromClass([[nc.viewControllers objectAtIndex:[nc.viewControllers count]-1] class]));
}
Jason Coco
Good, except it doesn't always work: setViewControllers:: will overwrite viewControllers property and if you're popping to the root you're going to understep the array boundary (which is of course easy to avoid, but the overwriting of the controller stack dunno!)
groundhog
Of course you should add guards for that, and you're not supposed to pop the root anyway. As for setViewControllers -- he specifically said that he was pushing in his case.
Jason Coco
+1  A: 

I don't think the answers that use the UINavigationControllerDelegate work because, as the question points out, by the time the delegate is called the view controller that will be displayed is already the value for navigationController.topViewController and navigationController.visibleViewController.

Instead, use observers.

Step 1. Set up an observer to watch for the UINavigationControllerWillShowViewController notification:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewControllerChange:) name:@"UINavigationControllerWillShowViewControllerNotification" object:self.navigationController];

Step 2. Create the notification callback (in this example called viewControllerChange) and use keys in the notifications userInfo dictionary to see the last and next view controllers:

(void)viewControllerChange:(NSNotification *)notification {
    NSDictionary *userInfo = [notification userInfo];
    NSLog(@"Switching from %@ to %@", [[userInfo objectForKey:@"UINavigationControllerLastVisibleViewController"] class], [[userInfo objectForKey:@"UINavigationControllerNextVisibleViewController"] class]);
}
Dan Grigsby
This really works. But I can't find any documentation on that notification, is it private? Will I get in trouble if I submit an app. using it?
leolobato
Nope. Won't get in trouble. Notifications are a core part of the API. Apple documents this in the "Cocoa Fundamentals Guide" in the section titled "Communicating with objects"
Dan Grigsby