iPhone导航问题 - 嵌套推动动画可能导致导航栏损坏

68

我一直收到以下错误:

2011-04-02 14:55:23.350 AppName[42430:207] nested push animation can result in corrupted navigation bar
2011-04-02 14:55:23.352 AppName[42430:207] nested push animation can result in corrupted navigation bar
2011-04-02 14:55:23.729 AppName[42430:207] Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
2011-04-02 14:55:23.729 AppName[42430:207] Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.

这是我的做法。从一个视图控制器中,当某个按钮被按下时,我调用以下代码:

EventsViewController *viewController = [[EventsViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
navController.navigationBar.tintColor = [UIColor blackColor];
[self presentModalViewController:navController animated:YES];
[viewController release];
[navController release];

然后,如果在EventsController中按下了某个按钮,我会调用:

SingleEventViewController *viewController = [[SingleEventViewController alloc] initWithEvent:[currentEvents objectAtIndex:indexPath.row]];
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];

然后,在SingleEventViewController中如果某个按钮被按下,我会调用:

EventMapView* viewController = [[EventMapView alloc] initWithCoordinates];
[[self navigationController] pushViewController:viewController animated:YES];
[viewController release];

显然存在嵌套的push动画,但这是正确的做法吗?我查看了苹果的DrillDownSave代码,这似乎是他们的做法。我使用init方法而不是viewDidLoad方法有关系吗?

20个回答

120

viewDidAppear之前调用pushViewController是不安全的。


1
这绝对是解决问题的说明。 - daniherculano
2
如果在次要线程上调用pushViewController,也会发生这种情况。 - Iris Artin
1
我明白这样的情况。但是,你能提供一些相关的文档吗?这并没有在UINavigationController 的文档中提到。 - pietrorea
@PietroRea 我认为文档中找不到这样的声明。然而,文档说pushViewController通常用于响应用户选择一个项目后即当前视图可用时使用。因此,重新排列这些事件可能会使内部FSM混淆,这是合乎逻辑的期望。 - S B
非常感谢你。你节省了我的时间。我正在使用展示菜单,当我在viewwillappear上推送viewcontroller时,一切都是错误的。真的非常感谢你 :) - Erhan Demirci

92

意外地触发了同一个segue两次 一次是在代码中,一次是在界面构建器中,但两次同时发生...

我遇到了和大家一样的错误。问题在于我意外地触发了同一个segue两次:一次是在界面构建器中,另一次是在我的代码中。

我有一个UITableView。当选择一个单元格时,界面构建器中的segue就会被触发。这就是我的问题所在,我在界面构建器中设置了segue直接从单元格本身开始触发,然后在我的代码中,在didSelectRowAtIndexPath下写了会触发同一个segue的代码...如下所示...

[self performSegueWithIdentifier:@"MySegue" sender:tableView];
那就是当didSelectRowAtIndexPath被调用时,因为一行被选中,它会触发上面那行代码的segue。然后界面生成器也会触发这个segue,因为它直接连接到界面生成器中的单元格对象。要停止界面生成器直接触发segue,你必须将segue从视图控制器的顶部连接,而不是在单元格本身内部嵌套下来。

所以如果你和我有同样的问题,即你调用了相同的segue两次,你可以通过取消从CELL DIRECTLY到segue的连接,并从IB中的表层级别开始链接segue来解决这个问题,而不是从单元格内嵌套。将segue从您的View Controller本身连接到segue。如果你做对了,当你选择segue时,它应该突出显示它来自的整个视图,而不仅仅是单元格。

现在苹果的文档在performSegueWithIdentifier:sender:参考文献下如此陈述:

应用程序通常不需要直接触发segues。相反,您配置与视图控制器相关联的界面生成器中的对象,例如嵌入在其视图层次结构中的控件,以触发segue。但是,您可以调用此方法以编程方式触发segue,可能是响应于无法在故事板资源文件中指定的某些操作。例如,您可能会从用于处理摇动或加速度事件的自定义操作处理程序中调用它。

在我的情况下,我有一个用于我的UITableView的搜索按钮,必须确定当搜索结果表存在或正常的表视图存在时是否调用segue。所以我需要直接触发segue。

因此,从界面生成器中删除嵌入的控件,将其粘贴到视图控制器本身上,然后在你的代码中触发segue!

现在,不再有双重segues!也没有错误了。


1
我的问题也是由于按钮的两次触发引起的。 - lomec
1
我在IB中为UICellView设置了一个segue,然后我实现了通过self.storyboard创建一个新的VC并将我的数据传递到该VC,然后通过[self.navigationController pushViewController:vc animated:YES];进行推送。我的解决方案是在IB中删除segue。 - Saad Masood
我没有使用故事板,而是使用pushviewcontroller。如何避免这个问题? - Gajendra K Chauhan

13

我刚才遇到了与你相同的问题/错误信息,正在寻找解决方法,最终进入了这个帖子,但是对于我来说,我发现解决方案实际上是在进行嵌套推送时仅使用一个 animated:YES(只为最后一个推送放置 animated:YES),希望这对你有所帮助。

祝好。


你的评论让我解决了难题——请看下面我对被接受答案的评论,它也可能在未来帮到你。 - Charles Randall
我没有收到错误提示,但是在从第三个视图弹出后,我的第一个视图的导航栏显示了中间视图的导航栏。不使用从顶部弹出的动画解决了这个问题。 - Bill
谢谢,当你提到它时,这就有了很多意义。我已经使用animated:YES推送了初始控制器,然后使用animated:NO-ily推送了其余的内容,以使用户到达正确的位置。 - Kalle

9

我已经弄清楚了。显然,如果您在UITableViewDelegate的-didSelectRowAtIndexPath方法之外调用pushViewController方法,它不起作用。将该调用移动到该函数中即可解决问题。很奇怪。


20
这个问题的解决方案在于,在视图完成动画之后才能调用didSelectRowAtIndexPath。如果你确保在viewDidAppear:或者更晚的时候调用pushViewController,iOS就不会抱怨有嵌套的pushViewController调用了。 - Charles Randall
我不是很理解“显然,如果你在UITableViewDelegate的-didSelectRowAtIndexPath方法之外调用-pushViewController,它就不起作用。”。但在我的项目中它是有效的。 - Henson Fang

8
我遇到了同样的问题,这是由于nib文件中的一个按钮连接了两个不同的操作。它尝试加载两个视图控制器,导致堆栈出问题。

3
我也遇到了类似的问题。在我的情况下,我混合使用了故事板转场和编程转场(performSegueWithIdentifier)。当我不小心同时执行两种方式时,就会出现这个错误。 - Symmetric
2
这也是我的问题。我有静态表格单元格,它们“推”到下一个控制器。我还在didSelectRowAtIndexPath中使用了performSegueWithIdentifier。两者都被调用,导致出现此消息。 - ekawas
我的问题几乎与此答案中描述的问题完全相同,只是在IB中,我不知何故为同一个按钮设置了两个相同的操作。我相信这是因为我在IB中使用了复制和粘贴。要小心。 - Alyoshak

4

当你说使用init方法而不是viewDidLoad方法时,你的意思是什么?

如果在旧的推送得到执行之前就推送了一个新的视图控制器,你就会遇到这种错误。因此,在init中放置特定的代码并过早地执行某些操作可能会导致报告的错误。

在运行视图控制器的init方法时,视图尚未加载!


1
我之所以说我使用init而不是viewDidLoad或loadView的唯一原因是因为Apple的示例应用程序没有使用init方法来设置视图,所以我认为这可能是其中的一部分。此外,即使我在init方法中注释掉了所有代码,我仍然会遇到这个问题。如果情况确实如此,为什么SingleEventViewController也不会出现这种情况呢? - joshholat
不要使用init来设置你的UI/视图项,那不是通常的地方。仔细查看一下UIViewController的文档。一个视图可以从nib文件加载。或者你可以在loadView中以编程方式进行加载。或者你可以通过nib文件设置基本视图,然后在viewDidLoad中以编程方式添加更多项。但你不应该在init中做这种事情。 - occulus
我会以编程的方式来实现它。所以我应该在loadView中分配内存并设置所有属性,然后再在init中将这些内容添加到视图中吗?因为如果我尝试在loadView方法中执行 [self.view addSubview:myTableView];,它会导致崩溃。 - joshholat
是的, 如果我们仅在ViewDidLoad方法中执行Perform Segue,它会产生上述错误,而且在下一个ViewControllers中,它们的contentViews会被破坏,它们将显示相同的旧From View Controller内容。所以我做的是,我从ViewDid Load中删除了performSegue方法调用,并将其放到了一个Button操作中,问题得到解决。我曾经尝试并浪费了ViewDid Load方法中的那个方法调用,只是为了测试目的!因为我需要在View加载后不久就这样做。所以我将其与ViewDidLoad分开。 - Randika Vishman

2

嗯,我遇到了这个问题,对iOS开发场景还很陌生。但是在界面构建器中查看我的连接检查器(与文件所有者),我发现当我复制一个按钮时,它除了新方法外还分配了先前按钮的方法。我想那就是我的问题嵌套的地方,因为它执行了两种不同的方法,都将一个视图推到了Nav Controller上。我知道这个问题已经得到了解答,但我想我会把这个问题放上来,以防其他人也像我一样犯了一个愚蠢的错误。


1
我遇到的问题与键盘活动有关。
这是由于我从textField的代理方法中推出了一个ViewController引起的:
-(void)textFieldDidBeginEditing:(UITextField *)textField{

        FilterLocationViewController *destViewController = (FilterLocationViewController *)[self.storyboard instantiateViewControllerWithIdentifier:@"FilterLocationViewController"];

        [self.navigationController pushViewController:destViewController animated:YES];

}

通过将代码更改为以下内容:
-(void)textFieldDidBeginEditing:(UITextField *)textField{

        [_textFieldLocation resignFirstResponder]; //adding this line

        FilterLocationViewController *destViewController = (FilterLocationViewController *)[self.storyboard instantiateViewControllerWithIdentifier:@"FilterLocationViewController"];

        [self.navigationController pushViewController:destViewController animated:YES];

}

在添加代码[textField resignFirstResponder];后,问题消失了。

基本上这个教训就是,如果键盘弹出来了,你不应该修改navigationController的栈。


1
在我的情况下,我遇到了同样的问题。我错过了switch语句中的一个break,所以两个segue同时触发了。对我来说很容易解决。

1

最近,我遇到了同样的问题。原因是:-我错误地尝试弹出视图控制器两次。您可以通过在推送和弹出视图控制器上设置断点来检查此崩溃。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接