[ios] Unbalanced calls to begin/end appearance transitions for <UITabBarController: 0x197870>

I read SO about another user encountering similar error, but this error is in different case.

I received this message when I added a View Controller initially:

Unbalanced calls to begin/end appearance transitions for 
<UITabBarController: 0x197870>

The structure of the app is as follow:

I got a 5-tab TabBarController linked to 5 View Controllers. In the initial showing tab, I call out a new View Controller to overlay as an introduction of the app.

I use this code to call the introduction view controller:

IntroVC *vc = [[IntroVC alloc] init];
[self presentModalViewController:vc animated:YES];
[vc release]; 

After this IntroVC view controller shows up, the above error shows.

p.s. I am using xCode 4.2 & iOS 5.0 SDK, developing iOS 4.3 app.

This question is related to ios ios4 uitabbarcontroller

The answer is


In Swift 2+ for me works:

I have UITabBarViewController in storyboard and I had selectedIndex property like this:

enter image description here

But I delete it, and add in my viewDidLoad method of my initial class, like this:

override func viewDidLoad() {
   super.viewDidLoad()
   self.tabBarController?.selectedIndex = 2
}

I hope I can help someone.


I had this problem when I had navigated from root TVC to TVC A then to TVC B. After tapping the "load" button in TVC B I wanted to jump straight back to the root TVC (no need to revisit TVC A so why do it). I had:

//Pop child from the nav controller
[self.navigationController popViewControllerAnimated:YES];
//Pop self to return to root
[self.navigationController popViewControllerAnimated:YES];

...which gave the error "Unbalanced calls to begin/end etc". The following fixed the error, but no animation:

//Pop child from the nav controller
[self.navigationController popViewControllerAnimated:NO];
//Then pop self to return to root
[self.navigationController popViewControllerAnimated:NO];

This was my final solution, no error and still animated:

//Pop child from the nav controller
[self.navigationController popViewControllerAnimated:NO];
//Then pop self to return to root, only works if first pop above is *not* animated
[self.navigationController popViewControllerAnimated:YES];

As @danh suggested, my issue was that I was presenting the modal vc before the UITabBarController was ready. However, I felt uncomfortable relying on a fixed delay before presenting the view controller (from my testing, I needed to use a 0.05-0.1s delay in performSelector:withDelay:). My solution is to add a block that gets called on UITabBarController's viewDidAppear: method:

PRTabBarController.h:

@interface PRTabBarController : UITabBarController

@property (nonatomic, copy) void (^viewDidAppearBlock)(BOOL animated);

@end

PRTabBarController.m:

#import "PRTabBarController.h"

@implementation PRTabBarController

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    if (self.viewDidAppearBlock) {
        self.viewDidAppearBlock(animated);
    }
}

@end

Now in application:didFinishLaunchingWithOptions:

PRTabBarController *tabBarController = [[PRTabBarController alloc] init];

// UIWindow initialization, etc.

__weak typeof(tabBarController) weakTabBarController = tabBarController;
tabBarController.viewDidAppearBlock = ^(BOOL animated) {
    MyViewController *viewController = [MyViewController new];
    viewController.modalPresentationStyle = UIModalPresentationOverFullScreen;
    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
    [weakTabBarController.tabBarController presentViewController:navigationController animated:NO completion:nil];
    weakTabBarController.viewDidAppearBlock = nil;
};

I had the same problem and thought I would post in case someone else runs into something similar.

In my case, I had attached a long press gesture recognizer to my UITableViewController.

UILongPressGestureRecognizer *longPressGesture = [[[UILongPressGestureRecognizer alloc]
                                                   initWithTarget:self
                                                   action:@selector(onLongPress:)]
                                                  autorelease];
[longPressGesture setMinimumPressDuration:1];
[self.tableView addGestureRecognizer:longPressGesture];

In my onLongPress selector, I launched my next view controller.

- (IBAction)onLongPress:(id)sender {

    SomeViewController* page = [[SomeViewController alloc] initWithNibName:@"SomeViewController" bundle:nil];

    [self.navigationController pushViewController:page animated:YES];

    [page release];

}

In my case, I received the error message because the long press recognizer fired more than one time and as a result, my "SomeViewController" was pushed onto the stack multiple times.

The solution was to add a boolean to indicate when the SomeViewController had been pushed onto the stack. When my UITableViewController's viewWillAppear method was called, I set the boolean back to NO.


I found that, if you are using a storyboard, you will want to put the code that is presenting the new view controller in viewDidAppear. It will also get rid of the "Presenting view controllers on detached view controllers is discouraged" warning.


I had the same issue. When developing I wanted to bypass screens. I was navigating from one view controller to another in viewDidLoad by calling a selector method.

The issue is that we should let the ViewController finish transitioning before transitioning to another ViewController.

This solved my problem: The delay is necessary to allow ViewControllers finish transitioning before transitioning to another.

self.perform(#selector(YOUR SELECTOR METHOD), with: self, afterDelay: 0.5)


I had this problem with a third party code. Someone forgot to set the super inside of viewWillAppear and viewWillDisappear in a custom TabBarController class.

- (void) viewWillAppear:(BOOL)animated {

    [super viewWillAppear:animated];
    // code...
}

or

- (void) viewWillDisappear:(BOOL)animated {

    [super viewWillDisappear:animated];
    // code...
}

I solved it by writing

[self.navigationController presentViewController:viewController 
                                        animated:TRUE 
                                      completion:NULL];

For me this error occurred because i didn't have UIWindow declared in the upper level of my class when setting a root view controller

            rootViewController?.showTimeoutAlert = showTimeOut
            let navigationController = SwipeNavigationController(rootViewController: rootViewController!)
            self.window = UIWindow(frame: UIScreen.main.bounds)
            self.window?.rootViewController = navigationController
            self.window?.makeKeyAndVisible()

Ex if I tried declaring window in that block of code instead of referencing self then I would receive the error


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];
    }
}

I encountered this error when I hooked a UIButton to a storyboard segue action (in IB) but later decided to have the button programatically call performSegueWithIdentifier forgetting to remove the first one from IB.

In essence it performed the segue call twice, gave this error and actually pushed my view twice. The fix was to remove one of the segue calls.

Hope this helps someone as tired as me!


I had the same problem. I called a method inside viewDidLoad inside my first UIViewController

- (void)viewDidLoad{
    [super viewDidLoad];

    [self performSelector:@selector(loadingView)
               withObject:nil afterDelay:0.5];
}

- (void)loadingView{

    [self performSegueWithIdentifier:@"loadedData" sender:self];
}

Inside the second UIViewController I did the same also with 0.5 seconds delay. After changing the delay to a higher value, it worked fine. It's like the segue can't be performed too fast after another segue.


As posted by danh

You can generate this warning by presenting the modal vc before the app is done initializing. i.e. Start a tabbed application template app and present a modal vc on top of self.tabBarController as the last line in application:didFinishLaunching. Warning appears. Solution: let the stack unwind first, present the modal vc in another method, invoked with a performSelector withDelay:0.0

Try to move the method into the viewWillAppear and guard it so it does get executed just once (would recommend setting up a property)


I had the same problem when I need to Present My Login View Controller from another View Controller If the the User is't authorized, I did it in ViewDidLoad Method of my Another View Controller ( if not authorized -> presentModalViewController ). When I start to make it in ViewDidAppear method, I solved this problem. I Think that ViewDidLoad only initialize properties and after that the actual showing view algorithm begins! Thats why you must use viewDidAppear method to make modal transitions!


I had the same error. I have a tab bar with 3 items and I was unconsciously trying to call the root view controller of item 1 in the item 2 of my tab bar using performSegueWithIdentifier.

What happens is that it calls the view controller and goes back to the root view controller of item 2 after a few seconds and logs that error.

Apparently, you cannot call the root view controller of an item to another item.

So instead of performSegueWithIdentifier

I used [self.parentViewController.tabBarController setSelectedIndex:0];

Hope this helps someone.


Swift 5

 func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {


//Delete or comment the below lines on your SceneDelegate.

//        guard let windowScene = (scene as? UIWindowScene) else { return }
//        window?.windowScene = windowScene
//        window?.makeKeyAndVisible()

        let viewController = ListVC()
        let navViewController = UINavigationController(rootViewController: viewController)
        window?.rootViewController = navViewController

    }

I fixed this error by changing animated from YES to NO.

From:

[tabBarController presentModalViewController:viewController animated:YES];

To:

[tabBarController presentModalViewController:viewController animated:NO];

If you're using transitioningDelegate (not the case in this question's example), also set modalPresentationStyle to .Custom.

Swift

let vc = storyboard.instantiateViewControllerWithIdentifier("...")
vc.transitioningDelegate = self
vc.modalPresentationStyle = .Custom

you need make sure -(void)beginAppearanceTransition:(BOOL)isAppearing animated:(BOOL)animated and -(void)endAppearanceTransition is create together in the class.


Another solution for many cases is to make sure that the transition between UIViewControllers happens after the not-suitable (like during initialization) procedure finishes, by doing:

__weak MyViewController *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
    [weakSelf presentViewController:vc animated:YES];
});

This is general for also pushViewController:animated:, etc.


I had lot of problem with the same issue. I solved this one by

  1. Initiating the ViewController using the storyboad instantiateViewControllerWithIdentifier method. i.e Intro *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"introVC"];
  2. [self.tabBarController presentModalViewController : vc animated:YES];

I have the viewcontroller in my storyboard, for some reason using only [[introvc alloc] init]; did not work for me.


I had this problem because of a typo:

override func viewDidAppear(animated: Bool) {
    super.viewWillAppear(animated)

instead of

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

It was calling "WillAppear" in the super instead of "DidAppear"


Examples related to ios

Adding a UISegmentedControl to UITableView Crop image to specified size and picture location Undefined Symbols error when integrating Apptentive iOS SDK via Cocoapods Keep placeholder text in UITextField on input in IOS Accessing AppDelegate from framework? Autoresize View When SubViews are Added Warp \ bend effect on a UIView? Speech input for visually impaired users without the need to tap the screen make UITableViewCell selectable only while editing Xcode 12, building for iOS Simulator, but linking in object file built for iOS, for architecture arm64

Examples related to ios4

Unbalanced calls to begin/end appearance transitions for <UITabBarController: 0x197870> How can we programmatically detect which iOS version is device running on? Convert a string into an int Xcode Objective-C | iOS: delay function / NSTimer help? How can I use NSError in my iPhone App? What programming languages can one use to develop iPhone, iPod Touch and iPad (iOS) applications? How to keep an iPhone app running on background fully operational How do you use NSAttributedString? How to parse the Manifest.mbdb file in an iOS 4.0 iTunes Backup

Examples related to uitabbarcontroller

Programmatically switching between tabs within Swift What size should TabBar images be? Unbalanced calls to begin/end appearance transitions for <UITabBarController: 0x197870> Switching to a TabBar tab view programmatically? Changing Tint / Background color of UITabBar