Aleksandar • Vacić

iOS bits and pieces

Beware: a crasher lurks with UINavigationController transitions

Custom view controller transitions are fast becoming my favourite iOS 7 API addition. As with all new things, there are surprises and side effects to learn about. This post is about one such issue, in this triangle:

There are 3 view controllers. First one (list) can either directly go to the 3rd (details), or it can first open the search controller in the middle, from where you can go to details as well. It’s all done through UINavigationController, using pushViewController:animated:.

Custom transitions exist only between 1st and 3rd VC and vice versa. Thus, both of this VCs are UINavigationControllerDelegate’s, as indicated by yellow circles and they contain this code:

1
2
3
4
5
6
7
8
9
10
11
12
- (void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
...    
  self.navigationController.delegate = self;
}

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC {
...
}

Search controller has no custom transition.

Crash scenario

These are the steps:

  1. on 1st (List) VC, tap to open Search VC
  2. on Seach VC, tap to open Details VC
  3. Go back to Search
  4. now, either try open another instance of Details VC or go back to List VC

App will crash on step 4.

Adding an Exception Breakpoint on throw, I got this stack trace:

Notice the second line from the top, just below objc_msgSend. Apparently it tries to get an instance of custom transition and crashes.

Solution

Rather simple - in Search VC, clear the UINC delegate.

1
2
3
4
5
- (void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
...    
  self.navigationController.delegate = nil;
}