在 prepareForSegue 完成之前调用 viewDidLoad
Posted
技术标签:
【中文标题】在 prepareForSegue 完成之前调用 viewDidLoad【英文标题】:viewDidLoad called before prepareForSegue finishes 【发布时间】:2013-02-15 08:32:33 【问题描述】:我的印象是 viewDidLoad 将在 prepareForSegue 完成后被调用。这甚至是 Hegarty 教授斯坦福课程的方式(最近是 2013 年 2 月)。
但是,今天第一次,我注意到 viewDidLoad 在 prepareForSegue 完成之前被调用。因此,我在 prepareForSegue 中设置的属性不适用于目标 viewDidLoad 方法中的目标视图控制器。
这似乎与预期的行为相反。
更新
我刚刚弄清楚发生了什么。在我的destinationViewController 中,我有一个自定义设置器,每次更新“模型”时都会重新加载tableView:
DestinationViewController
- (void)setManagedObjectsArray:(NSArray *)managedObjectsArray
_managedObjectsArray = [managedObjectsArray copy];
[self.tableView reloadData];
事实证明,由于destinationViewController 是UITableViewController 的子类...调用'self.tableView' 会强制加载视图。根据 Apple 的文档,调用视图控制器的 view 属性可以强制加载视图。 UITableViewController 的视图就是 tableView。
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html
因此,在 prepareForSegue 中,以下行强制加载 destinationViewController 的视图:
vc.managedObjectsArray = <custom method that returns an array>;
为了解决这个问题,我将destinationViewController的模型的自定义setter改为:
- (void)setManagedObjectsArray:(NSArray *)managedObjectsArray
_managedObjectsArray = [managedObjectsArray copy];
if ([self isViewLoaded])
[self.tableView reloadData];
这只会在 tableView 在屏幕上时重新加载 tableView。因此在 prepareForSegue 期间不会强制加载视图。
如果有人反对此过程,请分享您的想法。否则,我希望这可以防止某人度过一个漫长的不眠之夜。
【问题讨论】:
你怎么知道 viewDidLoad 被调用了?你能分享一些代码吗? 我怀疑prepareForSegue
实际上是(直接或间接)调用viewDidLoad
的函数。
【参考方案1】:
过去我也遇到过类似的困惑。我学到的一般经验法则是:
-
prepareForSegue 在目标 VC 的 viewDidLoad 之前调用
viewDidLoad 仅在所有出口都加载后调用
不要尝试在源 VC 的 prepareForSegue 中引用任何目标 VC 的出口。
关于 prepareForSegue 的另一个教训是避免冗余处理。例如,如果您已经通过情节提要将 tableView 单元格连接到 VC,如果您尝试同时处理 tableView:didSelectRowAtIndexPath 和 prepareForSegue,您可能会遇到类似的竞争条件。可以通过使用手动 segue 或放弃在 didSelectRowAtIndexPath 处的任何处理来避免这种情况。
【讨论】:
实际上如果你访问initWithCoder:
中的outlets,它在调用prepareForSegue:
之前会隐式调用viewDidLoad
方法。感谢@Hampde123 的评论,这对我有帮助
如果我们遵循这些规则,如何在视图控制器之间传递数据?【参考方案2】:
我想对这里发生的事情做一点解释:
在视图显示之前调用prepareForSegue
viewDidload
在第一次访问视图时被调用(视图是延迟加载的)。
可能发生的情况是您访问了prepareForSegue
中的view
,从而手动触发了视图加载。
通常流程是:
preformSegue
prepareForSegue
ViewController
被添加到层次结构中
viewDidLoad
。
但你的情况可能发生的是:
preformSegue
prepareForSegue
|— 在prepareForSegue
中,您访问view
|— view
被自动加载 => viewDidLoad
被调用
从performSegue
返回
ViewController 被添加到层次结构中,没有viewDidLoad
(已经调用)
所以是的,通常在将ViewController
添加到层次结构之前不会访问view
属性,但如果是,则可以更早地触发viewDidLoad
。
【讨论】:
这是有道理的,但是如果不是来自 prepareForSegue,你如何将参数传递给新视图? 还是可以的,但是访问视图前需要设置参数【参考方案3】:感谢分享,帮我解决了问题。
在我的例子中,destinationViewController 是一个 UITabBarController 并且修改它的 viewControllers 数组触发了 viewDidLoad:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
UITabBarController *tabBarController = segue.destinationViewController;
tabBarController.viewControllers = ...
tabBarController.something = something;
在 viewDidLoad 我需要设置 something 属性,所以我不得不将它向上移动:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
UITabBarController *tabBarController = segue.destinationViewController;
tabBarController.something = something;
tabBarController.viewControllers = ...
【讨论】:
【参考方案4】:简单的解决办法是放置
[self.tableView reloadData];
在viewWillAppear
:
【讨论】:
【参考方案5】:我的动态单元格有一个类似的问题,它在选择时显示了一个模式。 prepareForSegue
在 didSelect:atIndexPath
之前执行。对我有帮助的是,在情节提要中,我将 segue 重新分配为从控制器开始,而不是从动态单元原型开始。我解决了比赛条件(?),一切正常!
【讨论】:
【参考方案6】:在我的情况下,在 prepareSegue 中设置目标演示控制器的委托会导致调用 viewDidLoad。 我搬家了 segue.destination.presentationController?.delegate = self 到 prepareSegue 结束:
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
// do stuff ....
segue.destination.presentationController?.delegate = self
【讨论】:
以上是关于在 prepareForSegue 完成之前调用 viewDidLoad的主要内容,如果未能解决你的问题,请参考以下文章
只有在添加第三个 segue 时才在 didSelectRowAtIndexPath 之前调用 prepareForSegue
在 prepareForSegue 上为 Popover Segue 传递数据:iOS 5 中的奇怪行为
如何在 iOS 中的 prepareForSegue 之前执行按钮按下操作?