While investigating a memory leak I discovered a problem related to the technique of calling setRootViewController: inside a transition animation block:
I faced this issue and it annoyed me for a whole day. I've tried @Rich's obj-c solution and it turns out when I want to present another viewController after that, I will be blocked with a blank UITransitionView.
Finally, I figured out this way and it worked for me.
- (void)setRootViewController:(UIViewController *)rootViewController {
// dismiss presented view controllers before switch rootViewController to avoid messed up view hierarchy, or even crash
UIViewController *presentedViewController = [self findPresentedViewControllerStartingFrom:self.window.rootViewController];
[self dismissPresentedViewController:presentedViewController completionBlock:^{
[self.window setRootViewController:rootViewController];
}];
}
- (void)dismissPresentedViewController:(UIViewController *)vc completionBlock:(void(^)())completionBlock {
// if vc is presented by other view controller, dismiss it.
if ([vc presentingViewController]) {
__block UIViewController* nextVC = vc.presentingViewController;
[vc dismissViewControllerAnimated:NO completion:^ {
// if the view controller which is presenting vc is also presented by other view controller, dismiss it
if ([nextVC presentingViewController]) {
[self dismissPresentedViewController:nextVC completionBlock:completionBlock];
} else {
if (completionBlock != nil) {
completionBlock();
}
}
}];
} else {
if (completionBlock != nil) {
completionBlock();
}
}
}
+ (UIViewController *)findPresentedViewControllerStartingFrom:(UIViewController *)start {
if ([start isKindOfClass:[UINavigationController class]]) {
return [self findPresentedViewControllerStartingFrom:[(UINavigationController *)start topViewController]];
}
if ([start isKindOfClass:[UITabBarController class]]) {
return [self findPresentedViewControllerStartingFrom:[(UITabBarController *)start selectedViewController]];
}
if (start.presentedViewController == nil || start.presentedViewController.isBeingDismissed) {
return start;
}
return [self findPresentedViewControllerStartingFrom:start.presentedViewController];
}
Alright, now all you have to do is call [self setRootViewController:newViewController]; when you want to switch root view controller.