28

I have an UITabBarController, when initial run, I want to overlay a login view controller but received error.

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

Below is the code.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];

    // Override point for customization after application launch.

    UIViewController *lessonVC = [[[LessonViewController alloc] initWithNibName:@"LessonViewController" bundle:nil] autorelease];

    UIViewController *programVC = [[[ProgramViewController alloc] initWithNibName:@"ProgramViewController" bundle:nil] autorelease];

    UIViewController *flashcardVC = [[[FlashCardViewController alloc] initWithNibName:@"FlashCardViewController" bundle:nil] autorelease];

    UIViewController *moreVC = [[[MoreViewController alloc] initWithNibName:@"MoreViewController" bundle:nil] autorelease];

    UINavigationController *lessonNVC = [[[UINavigationController alloc] initWithRootViewController:lessonVC] autorelease];

    UINavigationController *programNVC = [[[UINavigationController alloc] initWithRootViewController:programVC] autorelease];

    UINavigationController *flashcardNVC = [[[UINavigationController alloc] initWithRootViewController:flashcardVC] autorelease];

    UINavigationController *moreNVC = [[[UINavigationController alloc] initWithRootViewController:moreVC] autorelease];

    self.tabBarController = [[[UITabBarController alloc] init/*WithNibName:nil bundle:nil*/] autorelease];
    self.tabBarController.viewControllers = [NSArray arrayWithObjects:lessonNVC, programNVC, flashcardNVC, moreNVC, nil];
    self.tabBarController.selectedIndex = 0;
    self.window.rootViewController = self.tabBarController;

    [self.window makeKeyAndVisible];

    if (![[ZYHttpRequest sharedRequest] userID]) 
    {
        // should register or login firstly
        LoginViewController *loginVC = [[LoginViewController alloc] initWithNibName:@"LoginViewController"
                                                                             bundle:nil];
        loginVC.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
        [self.tabBarController presentModalViewController:loginVC animated:YES];
        ZY_SAFE_RELEASE(loginVC);
    }

    return YES;
}

Anyone who can help me? Thanks in advance!

WrightsCS
  • 50,551
  • 22
  • 134
  • 186
ZYiOS
  • 5,204
  • 3
  • 39
  • 45

5 Answers5

78

You need to wait to present the modal view controller until the next run loop. I ended up using a block (to make things more simple) to schedule the presentation for the next run loop:

Update:
As mentioned by Mark Amery below, just a simple dispatch_async works, there's no need for a timer:

dispatch_async(dispatch_get_main_queue(), ^(void){     
  [self.container presentModalViewController:nc animated:YES]; 
});

/* Present next run loop. Prevents "unbalanced VC display" warnings. */
double delayInSeconds = 0.1;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    [self.container presentModalViewController:nc animated:YES];
});
Ozgur Vatansever
  • 49,246
  • 17
  • 84
  • 119
Maurizio
  • 4,143
  • 1
  • 29
  • 28
  • 4
    No need for a timer here, at least in the case I had (in which your answer solved my warning). Just do `dispatch_async(dispatch_get_main_queue(), ^(void){ [self.container presentModalViewController:nc animated:YES]; });` which is simpler and less hacky. – Mark Amery Sep 18 '13 at 15:27
  • I totally agree with Mark, it's easier just to use ` dispatch_async(dispatch_get_main_queue(), { code block }) – netwire Jul 24 '14 at 23:53
  • 7
    `dispatch_async()` didn't work on iOS8 for me but `dispatch_after()` worked. The downside of it is that I see the rootViewController for a moment (because of 0.1f delay). – SoftDesigner Sep 22 '14 at 11:41
  • @SoftDesigner I have the same problem, the rootViewController is shown for a moment. Did you find a solution to this? – entropid Oct 17 '14 at 03:12
  • @Entropid nope, I had to change the structure of view controllers to avoid that. – SoftDesigner Oct 17 '14 at 11:11
  • 1
    I'm using [[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]]; before presentViewController:, it looks better for me, since you are sure, that loop ran before presenting. – Povilas Dec 17 '14 at 11:57
  • @Entropid on iOS8 I had to delay the first presentation to `viewDidAppear` on the root view controller (which must be done loading by that point). Unfortunately there is a brief delay and, alas, `viewWillAppear` is too soon. – wcochran Jul 15 '15 at 20:24
10

I suspect the problem is that you're trying to call presentModalViewController: before the tab bar is done loading. Try moving the final logic onto the next event loop:

  [self.window makeKeyAndVisible];
  [self performSelector:(handleLogin) withObject:nil afterDelay:0];
}

- (void)handleLogin
{
  if (![[ZYHttpRequest sharedRequest] userID]) 
    {
        // should register or login firstly
        LoginViewController *loginVC = [[LoginViewController alloc] initWithNibName:@"LoginViewController"
                                                                             bundle:nil];
        loginVC.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
        [self.tabBarController presentModalViewController:loginVC animated:YES];
        ZY_SAFE_RELEASE(loginVC);
    }
}
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • [self performSelector:(handleLogin) withObject:nil afterDelay:0.1]; work in my iPod 4G, if delay time is 0, will only work on simulator but get the same warning in device. – ZYiOS Dec 28 '11 at 15:08
  • Thank you, thank you, thank you. The selected answer has the same idea, but much more complicated, and this worked great for me. – Le Mot Juiced Jul 20 '13 at 21:45
5
[self.tabBarController presentModalViewController:loginVC animated:**NO**];
danielbeard
  • 9,120
  • 3
  • 44
  • 58
Rio V
  • 59
  • 1
  • 1
2

I had a similar problem when tried to presentModalViewController (my welcome screen) in main view's viewWillAppear. Is was solved just by moving the modal VC call to viewDidAppear.

Arseniy
  • 487
  • 4
  • 14
0
[self performSelector:@selector(modaltheView) withObject:self afterDelay:0.1];
-(void)modaltheView
{
    [self.container presentModalViewController:nc animated:YES];
}
DD_
  • 7,230
  • 11
  • 38
  • 59
swamy
  • 9
  • 1