Intercepting UINavigationController back button

I was looking for a simple way to add a UINavigationBar to my a modal view controller that presents a normal (pointy) Back button as if there was a root view controller to go back to. It would be as if my outer view was the root view controller.

Now I know one legitimate way of doing this would be to wrap a UINavigationController around my existing outer view an hiding the navigation bar, but that would be an intrusive change to my view class that I didn’t want to change. Another way would be to create UINavigationBar and have my view controller manage it, but this seemed like extra work I was hoping to avoid. I was looking for a self-contained, sneaky way of using a UINavigationController within my modal view controller class’ code.

The two issues were: giving the navigation bar a pointy Back button, and getting called when the user presses the button.

Since it seemed impossible to create a left-pointing button on-demand, I figured the first issue could most easily be solved by making a fake root view controller then pushing my modal view controller onto the UINavigationController’s stack. To keep this detail private to my modal view controller, I made a public method containerViewController that assembles this UINavigationController & fake root view. My caller uses it like:


modalViewController = [[MyModalTableViewController alloc] init];
modalViewController.delegate = whatever;
// and other setup, notification observers added, and such
[myViewController presentModalViewController:
    [modalViewController containerViewController] animated:YES];
// rather than presenting modalViewController itself

To intercept the back button, I searched the net to find a solution. One of the top hits suggests subclassing UINavigationController and overriding - [UIViewController popViewControllerAnimated:]. This worked but I was saw that the UINavigationController’s pop animation back to the fake root controller was still happening, which was interfering with my delegate’s for dismissing the modal view. I needed to find another override that could prevent the pop animation.

I noticed that UINavigationController is documented to be the delegate of UINavigationBar, even though it isn’t declared to be in the header file. I figured this meant UINavigationController implemented the UINavigationBarDelegate methods, one of which is - [UINavigationBarDelegate navigationBar:shouldPopItem:] and I found that indeed my sneaky UINavigationController subclass did indeed get a call to this method.

Here the code I made to do what I wanted:


@interface UINavigationController (UndocumentedUINavigationBarDelegateMethod)
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;
@end
@interface SneakyNavigationController : UINavigationController
@end

@implementation MyModalTableViewController
. . .
- (UIViewController *)containerViewController {
  UIViewController *fakeRootView = [[[UIViewController alloc] init] autorelease];
  fakeRootView.navigationItem.title = NSLocalizedString(@"Back", @"Modal TableView Back button");
  MyModalTableViewController *navController = [[[MyModalTableViewController alloc] initWithRootViewController:fakeRootView] autorelease];
  navController.navigationBar.barStyle = UIBarStyleBlack;
  [navController pushViewController:self animated:NO];
  return navController;
}
@end

@implementation SneakyNavigationController
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
  UIViewController *viewController = nil;
  for (viewController in self.viewControllers)
    if (viewController.navigationItem == item)
      break;
  if ([viewController isKindOfClass:[MyModalTableViewController class]]) {
    [(ManageSocialAccountsViewController *)viewController close];
    return NO;
  }
  return [super navigationBar:navigationBar shouldPopItem:item];
}
@end

Advertisements

One Comment on “Intercepting UINavigationController back button”

  1. Thanks for this, I was just missing the property declaration part, so I was getting a compile error. Thanks again!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s