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

Posted

技术标签:

【中文标题】在 viewDidAppear 中推送视图控制器不起作用【英文标题】:Pushing view controller within viewDidAppear doesn't work 【发布时间】:2014-01-28 21:14:54 【问题描述】:

复制步骤

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

secondViewController.m:

- (void)viewDidAppear:(BOOL)animated

    [super viewDidAppear:animated];
    NSLog(@"DEBUG: second screen did appear");
    [self.navigationController pushViewController:[self.storyboard instantiateViewControllerWithIdentifier:@"thirdScreen"] animated:YES];

thirdViewController.m:

- (void)viewDidAppear:(BOOL)animated

    [super viewDidAppear:animated];
    NSLog(@"DEBUG: third screen did appear");

2) 使 firstViewController(又名故事板中的 firstScreen)成为导航控制器的根视图控制器。

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

注意事项

我尝试使用UINavigationControllerDelegate-( 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];
);

问题

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

其他有更多知识/经验的人可以启发我吗?

谢谢!

【问题讨论】:

【参考方案1】:

这是一个有趣的问题。我非常有信心,如果您在 firstViewController.m 中推送第二个视图控制器时设置 animated:YESfinal UI 状态将看起来像预期的那样,第三个屏幕的内容和标题都正确可见。

但是,这显然不是您想要的过渡效果。为什么animated 标志无论如何都会产生影响?

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

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

dispatch_async(dispatch_get_main_queue(), ^
    [self.navigationController pushViewController:[self.storyboard instantiateViewControllerWithIdentifier:@"thirdScreen"] animated:YES];
);

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

【讨论】:

我倾向于你的说法是真实的。回想起来,我确信这与过渡时间有关。谢谢。【参考方案2】:

不确定为什么这不完全有效,可能与时间有关。但是要将多个视图控制器推送到导航控制器上,首选方法是使用setViewControllers:animated:

NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[navigationController viewControllers]];
[viewControllers addObjectsFromArray:viewControllersToPush];
[navigationController setViewControllers:viewControllers animated:YES];

【讨论】:

我已经尝试过,如我的问题的注释部分所述。它没有帮助,因为我不能在一个电话中完成所有操作。第二个视图控制器有条件地推送第三个视图控制器。不过还是谢谢分享。

以上是关于在 viewDidAppear 中推送视图控制器不起作用的主要内容,如果未能解决你的问题,请参考以下文章

如何从导航堆栈中推送/弹出uiviewcontroller时收到警报

为啥 viewDidAppear 没有被触发?

空的初始视图控制器,未调用 viewWillAppear() 和 viewDidAppear()

viewDidAppear:在启动期间出现的模态视图控制器上调用两次

是否可以在不使用继承的情况下在viewDidAppear上调用某些代码

从 viewDidAppear 呈现时,模态弹出框控制器不显示其内容