UIViewController添加为子视图时,viewWillAppear不会被调用

33

我有一个UIViewController,我从另一个视图控制器中加载它,然后将其视图添加到UIScrollView中。

self.statisticsController = [self.storyboard instantiateViewControllerWithIdentifier:@"StatisticsViewController"];
self.statisticsController.match = self.match;

[self.scrollView addSubview:self.statisticsController.view];

我已经在统计视图控制器中放置了断点,viewDidLoad被调用了,但是viewWillAppear没有被调用。
这是因为我没有将它推送到层次结构中还是其他原因?

是的,如果一个视图被添加为子视图,viewDidAppear方法将不会被调用。你可以通过在父视图的viewDidAppear方法中手动调用它来解决这个问题。 - sbarow
1
https://dev59.com/sFXTa4cB1Zd3GeqP361B - sbarow
啊,我想那可能是情况。谢谢。 - Fogmeister
9个回答

54

在将 statisticsController 添加到某个控制器的视图中时,你应该将它作为子视图控制器添加。

self.statisticsController = [self.storyboard instantiateViewControllerWithIdentifier:@"StatisticsViewController"];
self.statisticsController.match = self.match;

[self.scrollView addSubview:self.statisticsController.view];
[self addChildViewController:self.statisticsController];
[self.statisticsController didMoveToParentViewController:self];

我不确定这样做是否会调用 viewDidAppear, 但是你可以在子控制器中覆盖 didMoveToParentViewController: 方法,那么这个方法会被调用,你可以把你原本放在 viewDidAppear 方法里的任何代码放在这里。


1
嘿!这个有效 :D 非常感谢!@sbarow 是的,它会触发viewWillAppear方法。 - Fogmeister
1
这只能在iOS 6上运行,在iOS 5上你必须手动调用viewWillAppear: - Craig Siemens
1
在 [self addChildViewController:self.statisticsController] 之前应该调用 [self.statisticsController willMoveToParentViewController:self]。 - Rafał Augustyniak
问题是...它会触发两次viewWillAppear!!这太糟糕了。 - Fattie
9
实际上你必须先添加子控制器,然后再添加子视图。 - Alexander Volkov
显示剩余3条评论

48

我再次遇到了-viewWillAppear:未被调用的问题。在搜索后,我来到了这里。经过一些测试,我发现-addSubview-addChildViewController:的调用顺序很重要。

情况1:会触发控制器的-viewWillAppear:方法,但是情况2不会调用-viewWillAppear:方法。

情况1:

  controller?.willMoveToParentViewController(self)

  // Call addSubview first
  self.scrollView.addSubview(controller!.view)
  self.addChildViewController(controller!)

  controller!.didMoveToParentViewController(self)

案例2:

  controller?.willMoveToParentViewController(self)

  // Call adChildViewController first      
  self.addChildViewController(controller!)      
  self.scrollView.addSubview(controller!.view)

  controller!.didMoveToParentViewController(self)

7
有趣。这与苹果公司的视图控制器编程指南中的文档相矛盾。您应该提交一个错误报告。 - Nikolai Ruhe
这帮助了我。 - Rachit Rawat
1
这个有更新了吗?对我来说似乎是个bug,但我也遇到了同样的问题。苹果的文档说明第二种情况是正确的方式,而且更有意义(https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html#//apple_ref/doc/uid/TP40007457-CH11-SW13)。也许我会提交一个radar... - SeeMeCode
@SeeMeCode,请提交雷达报告。 :) 我已经有1年没有碰过那些代码了。 - AechoLiu
2
我可以补充一下,这个 bug 在 iOS 10 上发生过。但在后来的 iOS 11 上运行良好。 - Gustaf Rosenblad
显示剩余3条评论

22

默认情况下,外观回调会自动转发给子视图控制器。这是通过shouldAutomaticallyForwardAppearanceMethods属性确定的。检查此属性的值,如果它为NO,并且您的子视图控制器应该显示在容器控制器上,则应在容器控制器生命周期实现中使用以下方法通知子视图控制器:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    for (UIViewController *child in self.childViewControllers) {
        [child beginAppearanceTransition:YES animated:animated];
    }
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self.child endAppearanceTransition];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    for (UIViewController *child in self.childViewControllers) {
        [child beginAppearanceTransition:NO animated:animated];
    }
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [self.child endAppearanceTransition];
}

自定义外观和旋转回调行为

解决了我的问题!希望它对您也有所帮助。


6

如另一个答案中所述,当shouldAutomaticallyForwardAppearanceMethods设置为false时,父视图控制器可能不会调用viewWillAppear等方法。UINavigationController和UITabBarController也被认为是这样做的。在这种情况下,当视图出现并反之时,您需要调用子视图控制器上的beginAppearanceTransition(_ isAppearing: Bool, animated: Bool)方法,并将isAppearing设置为true。

您必须在代码的适当位置放置这些调用,通常当添加和删除子视图控制器时。

当您的自定义转换结束时,请不要忘记调用endAppearanceTransition方法,否则将不调用viewDidAppear和viewDidDisappear方法。


3
根据苹果公司的要求(https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html),添加子视图控制器的正确API调用顺序为:
[self addChildViewController:childVC];
[self.view addSubview:childVC.view];
[childVC didMoveToParentViewController:self];

但我仍然遇到了一个问题,即子视图控制器中的viewWillAppear有时不会被调用。我的问题在于存在一种竞态条件,可能导致上述代码在容器视图控制器中的viewDidAppear被调用之前执行。确保已经调用了viewDidAppear(或将子VC的添加推迟到它被调用时)对我解决了这个问题。

1
检查您的父VC是否为UINavigationViewController(或任何其他容器)。在这种情况下,shouldAutomaticallyForwardAppearanceMethods为False,并且不会调用外观方法。

1
之前的回答是正确的,但如果有人需要帮助——如果您在子视图控制器中覆盖了loadView,则不会调用任何其他UIViewController方法。
花了我一些时间才意识到为什么我的代码无法正常运行,直到我意识到我错误地覆盖了loadView而不是viewDidLoad

0

如果您的视图控制器尚未加载其视图,则也不会转发查看外观方法。 如果您在子视图控制器中覆盖了loadView,并且该视图已添加到视图层次结构中,则可能会发生这种情况。

在这种情况下,您可以执行以下操作:

addChild(childVC)
childVC.loadViewIfNeeded()
childVC.didMove(toParent: self)

0

我无法理解你的问题和描述。

我的问题与此类似,只不过是这样的。

CustomTabBarController -> CustomUINavigationController -> RootViewcontroller

除非您切换到另一个选项卡并返回,否则不会调用CustomUINavigationController和RootViewController的viewWillAppear方法。

解决方案是调用super.viewWillAppear(animated:true)。

override func viewWillAppear(_ animated: Bool) {
    **super.viewWillAppear(true)**
}

我为这个小错误挣扎了一天多。


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