"Unbalanced calls to begin/end appearance transitions for DetailViewController" when pushing more than one detail view controller

Ios

Ios 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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGruntcakesView Question on Stackoverflow
Solution 1 - IosrailwayparadeView Answer on Stackoverflow
Solution 2 - IosinfiniteLoopView Answer on Stackoverflow
Solution 3 - IosGabrielView Answer on Stackoverflow
Solution 4 - IosNicolas MiariView Answer on Stackoverflow
Solution 5 - Iosinfinite-loopView Answer on Stackoverflow
Solution 6 - IosymutluView Answer on Stackoverflow
Solution 7 - IosTimView Answer on Stackoverflow
Solution 8 - Ioslakum vishalView Answer on Stackoverflow
Solution 9 - IosIoannis KousisView Answer on Stackoverflow
Solution 10 - IosberbieView Answer on Stackoverflow
Solution 11 - IosRajesh NasitView Answer on Stackoverflow
Solution 12 - IosPedro GóesView Answer on Stackoverflow
Solution 13 - IosEva MadrazoView Answer on Stackoverflow