"Unbalanced calls to begin/end appearance transitions for DetailViewController" when pushing more than one detail view controller
IosIos Problem Overview
I have a view controller that contains a table view, the items in the table can be selected and a detail view controller duly created.
The items in the table represent items that can have a time based trigger associated with them and a local notification is scheduled for each item, if the app is in the foreground when a local notification expires then the detail view for the item is automatically displayed.
I have a problem that manifests when two notifications expire at the same time which results in the views not being displayed properly and in addition the console logs: "Unbalanced calls to begin/end appearance transitions for NNN" where NNN is my detail view controller.
The table view controller is created as follows:
self.tableViewController = [[TableViewController alloc] initWithNibName:@"TableView" bundle:nil];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:self.tableViewController];
self.window.rootViewController = navController;
When a local notification expires and didReceiveLocalNotification: is invoked the app broadcasts a notification using NSNotifcationCenter postNotificationName: and to which the table view controller is listening for. When the table view controller receives that notification it creates the detail view controller and pushes it to the stack as:
[self.navigationController pushViewController:detailViewController animated:YES];
I read somewhere that there could be a problem if a view controller pushes another view controller when it itself is not on the top of the stack - so I thought this must be the problem, because when the table view controller receives the 2nd notification it will no longer be on the top of the navigation stack because it will have previously just pushed a detail view controller onto the stack when the first notification arrived.
So I changed the push code to this:
[[self.navigationController topViewController].navigationController pushViewController:detailController animated:YES];
But it made no difference.
So I next thought there could be a problem because the first detail view controller was not getting the chance to fully display before the 2nd view controller was pushed - so I changed my app's notification posting from using:
[[NSNotificationCenter defaultCenter] postNotificationName:
to
[[NSNotificationQueue defaultQueue] enqueueNotification: postingStyle:NSPostWhenIdle]
So that the pushes wouldn't occur within the same iteraction of the app loop. But that made no difference, nor did attempting to introduce a delay to the pushing of the detail view controlle:
double delayInSeconds = 0.1;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[[self.navigationController topViewController].navigationController pushViewController:detailController animated:YES];
});
I've no idea what the problem is or what to try next, any ideas?
Ios Solutions
Solution 1 - Ios
> "The unbalanced calls to begin/end appearance transitions"
occurs when you try and display a new viewcontroller before the current view controller is finished displaying. You can reproduce it by navigating in viewWillAppear.
Basically you are trying to push two view controllers onto the stack at almost the same time. Suggest you maintain a queue in the tableview controller which maintains a list of the detail views which need displaying. Push one at a time on to the stack and check on exit from the current detail view whether there are any queued detail views which need displaying.
This kind of navigation is going to be confusing for the user. It may be better to consider making your detail view support multiple items.
Solution 2 - Ios
'Unbalanced calls to begin/end appearance transitions for '
Says an animation is started before the last related animation isnt done. So, are you popping any view controller before pushing the new one ? Or may be popping to root ? if yes try doing so without animation. i.e,
[self.navigationController popToRootViewControllerAnimated:NO];
And see if this resolves the issue, In my case this did the trick.
Solution 3 - Ios
"The unbalanced calls to begin/end appearance transitions" error occurs when you try and display a new viewcontroller before the current view controller is finished displaying.
So, you have to be sure you wont present a new VC until the first finishes its animations.
Use didShowViewController and willShowViewController to block presenting a new VC before the old one completes its animation. It's for backButtonAction who makes a popViewController with Animation: YES.
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[self.myNavView.backButton addTarget:self action:@selector(backButtonAction) forControlEvents:UIControlEventTouchUpInside];
}
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[self.myNavView.backButton removeTarget:self action:@selector(backButtonAction) forControlEvents:UIControlEventTouchUpInside];
}
Solution 4 - Ios
In my case, I was implementing a custom container view controller, and I was getting the warning when swapping child view controllers, despite the fact that I was calling both beginAppearanceTransition(_:animated:)
and endAppearanceTransition()
on both the entering and the exiting view controllers.
I solved it by reordering the method calls for the entering view controller; from:
addChildViewController(newContent)
targetContainer.insertSubview(newContent.view, at: 0)
newContent.beginAppearanceTransition(true, animated: animated)
// Called AFTER adding subview
to:
// Called BEFORE adding subview
newContent.beginAppearanceTransition(true, animated: animated)
addChildViewController(newContent)
targetContainer.insertSubview(newContent.view, at: 0)
Solution 5 - Ios
It can also happen when you try to pop a VC from the stack more than once. On my case, the method that popped the VC was mistakenly being called multiple times. Once I cleaned that up the issue went away.
Solution 6 - Ios
Actually you need to wait till the push animation ends. So you can delegate UINavigationController and prevent pushing till the animation ends.
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
waitNavigation = NO;
}
-(void)showGScreen:(id)gvc{
if (!waitNavigation) {
waitNavigation = YES;
[_nav popToRootViewControllerAnimated:NO];
[_nav pushViewController:gvc animated:YES];
}
}
Solution 7 - Ios
You can add a breakpoint to
-[UIViewController _endAppearanceTransition:]
from where UIKit prints
"Unbalanced calls to begin/end appearance transitions for %@."
Solution 8 - Ios
As mentioned in above answers this error accuses when we try to push a view controller before the previous view controller is finish loading, so one solution is that try to push controller after it finish loading, i.e. move your push view controller code from viewWillAppear
to viewDidAppear
Solution 9 - Ios
Look at these overloads:
If they are empty, then remark them and rebuild.
- (void) beginAppearanceTransition:(BOOL) isAppearing animated:(BOOL)animated {}
- (void) becomeActive:(NSNotification *) notification {}
Solution 10 - Ios
In my case the problem is that I am presenting a viewController (alert) on an already presented viewController (full screen sheet).
UIKit walks up the viewController parent chain but not the presenting chain so there is a mismatch when reaching the presented and the window root view controller.
Manually calling beginAppearanceTransition
in these cases made the message go away, though is seems rather a patch for a symptom than a real remedy for the error.
Solution 11 - Ios
In React native I am getting this error while opening galley or camera screen. I set timeout before opening this screen and its works for me.
Solution 12 - Ios
My UINavigationController
was being presented, so had to replace UIModalPresentationCurrentContext
with UIModalPresentationFullScreen
on the presentation mechanism. Or you can use UIModalPresentationAutomatic
if you have iOS 13.0 or more recent.
Solution 13 - Ios
Are you using the appearance proxy feature?
I have found very problematic this feature, last time I have a
> "Unbalanced calls to begin/end appearance transitions for"
I solved it removing the [[UITextField appearance] ..]
methods.