0

I just got a crash showing as the following screen shot, it happened when I click the back button on the navigation bar, is any typical situation will cause this crash?

enter image description here

Dima
  • 23,484
  • 6
  • 56
  • 83
Pei
  • 460
  • 4
  • 21

1 Answers1

2

In my experience there was an issue introduced in iOS 7 making it possible for you to start a transition before another has ended, which ultimately causes this crash. You can reproduce this manually if you put 2 navigation calls back to back and run them, such as:

[self.navigationController pushViewController:whatever animated:YES];
[self.navigationController pushViewController:whatever2 animated:YES];

If you do this, you will eventually see that crash occur.

The easiest way I found to make sure this never happens is by subclassing UINavigationController and implementing the UINavigationControllerDelegate to prevent overlapping transitions.

Once I began using the code below, the number of crashes I see due to this issue has dropped to 0.

One thing to note is that if you actually need to implement another <UINavigationControllerDelegate> you will need to write some code to store the extra delegate yourself and pass on the delegate calls, perhaps using NSProxy or something like that.

@interface MyNavigationController () <UINavigationControllerDelegate>
{
    // used to prevent "can't add self as subview" crashes which occur when trying to animate 2 transitions simultaneously
    BOOL _currentlyAnimating;
}

@end

@implementation MyNavigationController

- (void) viewDidLoad
{
    [super viewDidLoad];
    self.delegate = self;
}

- (void) pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if(_currentlyAnimating)
    {
        return;
    }
    else if(animated)
    {
        _currentlyAnimating = YES;
    }

    [super pushViewController:viewController animated:animated];
}

- (UIViewController *) popViewControllerAnimated:(BOOL)animated
{
    if(_currentlyAnimating)
    {
        return nil;
    }
    else if(animated)
    {
        _currentlyAnimating = YES;
    }

    return [super popViewControllerAnimated:animated];

}

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    _currentlyAnimating = NO;
}

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    // tracking cancelled interactive pop
    // http://stackoverflow.com/questions/23484310/canceling-interactive-uinavigationcontroller-pop-gesture-does-not-call-uinavigat
    [[self transitionCoordinator] notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> context)
    {
        if([context isCancelled])
        {
            UIViewController *fromViewController = [context viewControllerForKey:UITransitionContextFromViewControllerKey];
            [self navigationController:navigationController willShowViewController:fromViewController animated:animated];

            if([self respondsToSelector:@selector(navigationController:didShowViewController:animated:)])
            {
                NSTimeInterval animationCompletion = [context transitionDuration] * [context percentComplete];

                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (uint64_t)animationCompletion * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
                    [self navigationController:navigationController didShowViewController:fromViewController animated:animated];
                });
            }


        }
    }];
}

@end
Dima
  • 23,484
  • 6
  • 56
  • 83