Using setViewController from UINavigationController on iPhone doesn't behave properly

qwertfisch picture qwertfisch · Dec 3, 2010 · Viewed 10.2k times · Source

I'm having a problem with an iPhone App using UINavigationController. When I'm using pushNavigationController, it works fine. The iPhone does its animation while switching to the next ViewController. But when using an array of ViewControllers and the setViewControllers method, it has a glitch in the animation which can grow into a clearly visible animation bug.

The following snippet is called in the root ViewController. Depending on a condition it should either switch to ViewController1, or it should directly go to ViewController2. In the latter case the user can navigate back to vc1, then to the root.

NSMutableArray* viewControllers = [NSMutableArray arrayWithCapacity:2];
// put us on the stack
[viewControllers addObject:self];
// add first VC
AuthentificationViewController* authentificationViewController =
  [[[AuthentificationViewController alloc] initWithNibName:@"AuthentificationViewController" bundle:nil] autorelease];
[viewControllers addObject:authentificationViewController];

if (someCondition == YES)
{
 UserAssignmentsListViewController* userAssignmentsListViewController =
      [[[UserAssignmentsListViewController alloc] initWithNibName:@"UserAssignmentsOverviewViewController" bundle:nil] autorelease];

 [viewControllers addObject:userAssignmentsListViewController];
}

[self.navigationController
  setViewControllers:[NSArray arrayWithArray:viewControllers] animated:YES];

As you can see I'll add the first and maybe the second VC to the array, finally setting the navigationController stack with animation. This works properly if I only add the first controller. But in the case where the animation should go to the 2nd controller, the navigation bar's title won't be "flying in". Instead there is an empty title until the animation is finished. And, even worse, if I replace the navbar title with a custom button, this button will be displayed in the upper left corner until the animation is finished. That's quite a displaying bug.

I tried to use a workaround with multiple pushViewController methods, but the animation doesn't look / feel right. I want the navigation to do its animation in the same way as pushViewController does. The only difference here is, that I don't add a VC but set the whole stack at once. Is there another workaround here, or could this be considered as a framework's bug? I thought about using only pushNavController for VC2, then somehow insert VC1 into the stack, but that doesn't seem possible.

Thanks for all hints and advices. :-)

Technical data: I'm using iOS 4.2, compiling for 4.0.

Answer

qwertfisch picture qwertfisch · Jun 15, 2011

Finally I found the solution. The mistake was that the new top-level NavigationController has not been initialized and loaded properly until the animation is done. In my case, UserAssignmentsListViewController has a viewDidLoad method that will not be called until animation is done, but it sets the navigation title (here: a UIButton). Therefore the animation fails.

The solution is to refer to an already initialized view controller when it comes to pushing it to the stack. So initialize our top-level VC somewhere:

// initialize our top-level controller
ViewController* viewController2 = [[[ViewController alloc]
    initWithNibName:@"ViewController" bundle:nil] autorelease];

Then when pushing two or more VCs to the stack, the top level one is already initialized and the animation works (following the example from my original question):

NSMutableArray* viewControllers = [NSMutableArray arrayWithCapacity:2];
// put us on the stack, too
[viewControllers addObject:self];

ViewController* viewController1 = [[[ViewController alloc]
    initWithNibName:@"ViewController" bundle:nil] autorelease];
[viewControllers addObject:viewController1];

if (someCondition == YES)
{
    [viewControllers addObject:viewController2];
}

[self.navigationController
    setViewControllers:[NSArray arrayWithArray:viewControllers] animated:YES];