在viewDidAppear中推送视图控制器不起作用 [英] Pushing view controller within viewDidAppear doesn't work

查看:100
本文介绍了在viewDidAppear中推送视图控制器不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

重现步骤



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



firstViewController.m:

   - (void)viewDidAppear:(BOOL)animated 
{
[super viewDidAppear:animated];
NSLog(@DEBUG:第一个屏幕确实出现了);
[self.navigationController pushViewController:[self.storyboard instantiateViewControllerWithIdentifier:@secondScreen] animated:NO];
}

secondViewController.m:

   - (void)viewDidAppear:(BOOL)animated 
{
[super viewDidAppear:animated];
NSLog(@DEBUG:第二个屏幕确实出现了);
[self.navigationController pushViewController:[self.storyboard instantiateViewControllerWithIdentifier:@thirdScreen] animated:YES];
}

thirdViewController.m:

   - (void)viewDidAppear:(BOOL)animated 
{
[super viewDidAppear:animated];
NSLog(@DEBUG:第三个屏幕确实出现了);
}

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



3)运行app和请注意导航栏已更新以显示第三个屏幕的标题,但仍显示第二个屏幕的内容。



注释



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



我还尝试手动设置导航控制器的 viewControllers ,认为它会跳过一些此视图控制器处于活动状态逻辑并允许有问题的推送到工作,但它没有。



S.解决方案



我能想到的唯一解决方案是使用延迟调用在 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答案,它可能与运行循环有关,但我无法确认或否认(似乎可能因为调度推送允许它工作)。 / p>

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



谢谢!

解决方案

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



然而,这显然不是你瞄准的过渡效果对于。为什么动画标志会产生不同的差异?



如果你在<$中设置一个断点c $ c> -viewDidAppear:并查看堆栈跟踪,了解动画== YES 动画的情况==没有,它在我看来像动画==否 -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:仅表示转换的真正结束时动画,但似乎确实如此。


Steps to Reproduce

1) Create a navigation controller and 3 view controllers.

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) make firstViewController (aka firstScreen in storyboard) the root view controller of the navigation controller.

3) Run app and notice that the navigation bar has updated to show the third screen's title, but still shows the second screen's content.

Notes

I've tried using UINavigationControllerDelegate's -( void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated method since it seems to fire after the viewDidAppear method, but it didn't fix the issue.

I also tried manually setting the navigation controller's viewControllers thinking it would skip some "this view controller is active" logic and allow the problematic push to work, but it didn't.

Solution

The only solution I could come up with was using a delayed call to push the desired view controller in 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];
});

Problem

I'd like to understand why this is not working as expected. Based on some other SO answers I've seen on semi-relevant questions, it may have something to do with a run loop, but I can't confirm or deny that (seems possible since dispatching the push allows it to work).

Can anyone else with more knowledge/experience enlighten me?

Thanks!

解决方案

This is an interesting question. I'm pretty confident that if you were to set animated:YES when pushing the second view controller in firstViewController.m, the final UI state would look as expected, with the third screen's content and title both correctly visible.

However, this is clearly not the transition effect you're aiming for. And why does the animated flag make an iota of difference anyway?

If you set a break point in -viewDidAppear: and look at the stack traces both for the case where animated == YES and animated == NO, it looks to me like when animated == NO, -viewDidAppear: is invoked during a view layout operation in the UINavigationController. My money's on this being the reason that you're final view looks incorrect; performing a push now would be doing so before the previous push has completely finished.

This is where run loop considerations come in. We want the UINavigationController's view layout (which is happening on the main run loop's current loop cycle) to finish before asking for the next push. A simple way to achieve that is to queue the push to happen on the next cycle of the main run loop. A delay will certainly do the trick (I believe a delay of 0 is enough to delay to the next run loop cycle, so you could try replacing 250 * USEC_PER_SEC with 0). Another way would be to dispatch the action onto the main queue:

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

So my answer is kind of speculative, but based on some evidence. It feels slightly unsatisfactory that, when performing UINavigationController transitions, -viewDidAppear: only indicates the true end of the transition when it's animated, however that does seem to be the case.

这篇关于在viewDidAppear中推送视图控制器不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆