在viewDidAppear中推送视图控制器不起作用

Bre*_*den 5 uinavigationcontroller ios

重现步骤

1)创建导航控制器和3个视图控制器.

firstViewController.m:

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    NSLog(@"DEBUG: first screen did appear");
    [self.navigationController pushViewController:[self.storyboard instantiateViewControllerWithIdentifier:@"secondScreen"] animated:NO];
}
Run Code Online (Sandbox Code Playgroud)

secondViewController.m:

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    NSLog(@"DEBUG: second screen did appear");
    [self.navigationController pushViewController:[self.storyboard instantiateViewControllerWithIdentifier:@"thirdScreen"] animated:YES];
}
Run Code Online (Sandbox Code Playgroud)

thirdViewController.m:

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    NSLog(@"DEBUG: third screen did appear");
}
Run Code Online (Sandbox Code Playgroud)

2)制作firstViewController(故事板中的firstScreen)导航控制器的根视图控制器.

3)运行应用程序并注意导航栏已更新以显示第三个屏幕的标题,但仍显示第二个屏幕的内容.

笔记

我已经尝试过使用UINavigationControllerDelegate's -( void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated方法,因为它似乎在viewDidAppear方法之后触发,但它没有解决问题.

我也尝试手动设置导航控制器的viewControllers想法,它会跳过一些"这个视图控制器是活动的"逻辑,并允许有问题的推送工作,但它没有.

我能想到的唯一解决方案是使用延迟调用在secondViewController.m中推送所需的视图控制器:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 250 * USEC_PER_SEC), dispatch_get_main_queue(), ^{
    [self.navigationController pushViewController:[self.storyboard instantiateViewControllerWithIdentifier:@"thirdScreen"] animated:YES];
});
Run Code Online (Sandbox Code Playgroud)

问题

我想知道为什么这不能按预期工作.基于我在半相关问题上看到的其他一些SO答案,它可能与运行循环有关,但我无法确认或否认(似乎可能因为调度推送允许它工作).

有更多知识/经验的人能够启发我吗?

谢谢!

ste*_*yde 4

这是个有趣的问题。我非常有信心,如果您在firstViewController.manimated:YES中推送第二个视图控制器时进行设置,最终的UI 状态将按预期显示,第三个屏幕的内容和标题都正确可见。

然而,这显然不是您想要的过渡效果。为什么animated旗帜会产生一点点不同呢?

如果您设置断点并查看和-viewDidAppear:情况下的堆栈跟踪,在我看来,就像在 中的视图布局操作期间调用 , 时一样。我的钱就在这,因为你的最终观点看起来不正确;现在执行推送将在上一次推送完全完成之前执行。animated == YESanimated == NOanimated == NO-viewDidAppear:UINavigationController

这就是运行循环考虑因素的所在。我们希望UINavigationController的视图布局(发生在主运行循环的当前循环周期上)在请求下一次推送之前完成。实现此目的的一个简单方法是将推送排队在主运行循环的下一个周期发生。延迟肯定会解决问题(我相信延迟0足以延迟到下一个运行循环周期,因此您可以尝试替换250 * USEC_PER_SEC0)。另一种方法是将操作分派到主队列上:

dispatch_async(dispatch_get_main_queue(), ^{
    [self.navigationController pushViewController:[self.storyboard instantiateViewControllerWithIdentifier:@"thirdScreen"] animated:YES];
});
Run Code Online (Sandbox Code Playgroud)

所以我的回答有点推测,但基于一些证据。感觉有点不太令人满意的是,在执行UINavigationController过渡时,-viewDidAppear:仅在动画时指示过渡的真正结束,但情况似乎确实如此。