在 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:YES
,final UI 状态将看起来像预期的那样,第三个屏幕的内容和标题都正确可见。
但是,这显然不是您想要的过渡效果。为什么animated
标志无论如何都会产生影响?
如果您在-viewDidAppear:
中设置断点并查看animated == YES
和animated == 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时收到警报
空的初始视图控制器,未调用 viewWillAppear() 和 viewDidAppear()
viewDidAppear:在启动期间出现的模态视图控制器上调用两次