118

我已经阅读了很多关于人们在viewWillAppear没有正确创建视图层次结构时遇到问题的帖子。我的问题是我无法弄清楚这意味着什么。

如果我在该控制器上创建RootViewController并调用addSubView,我希望添加的视图能够连接到viewWillAppear事件。

有没有人有一个复杂的编程视图层次结构的例子,它成功地接收viewWillAppear了每个级别的事件?

苹果的文档状态:

警告:如果属于视图控制器的视图直接添加到视图层次结构中,视图控制器将不会收到此消息。如果您将视图插入或添加到视图层次结构中,并且它具有视图控制器,则应直接向关联的视图控制器发送此消息。未能向视图控制器发送此消息将阻止显示任何关联的动画。

问题是他们没有描述如何做到这一点。“直接”是什么意思?您如何“间接”添加视图?

我对 Cocoa 和 iPhone 还很陌生,所以如果除了基本的 Hello World 废话之外,还有来自 Apple 的有用示例,那就太好了。

4

27 回答 27

56

如果您使用导航控制器并设置其委托,则不会调用 view{Will,Did}{Appear,Disappear} 方法。

您需要改用导航控制器委托方法:

navigationController:willShowViewController:animated:
navigationController:didShowViewController:animated:
于 2008-10-17T04:35:39.453 回答
28

我遇到了同样的问题。只需viewWillAppear在将其添加为子视图之前向您的视图控制器发送一条消息。(有一个 BOOL 参数告诉视图控制器它是否被动画显示。)

[myViewController viewWillAppear:NO];

查看节拍器示例中的 RootViewController.m。

(我实际上发现 Apple 的示例项目很棒。除了 HelloWorld 之外还有很多;)

于 2008-09-28T01:20:27.820 回答
18

我终于找到了一个可行的解决方案!

UINavigationControllerDelegate

我认为它的要点是将导航控件的委托设置为它所在的视图控制器,并实现UINavigationControllerDelegate它是两种方法。杰出的!我很兴奋,我终于找到了解决方案!

于 2010-12-22T10:38:50.490 回答
11

感谢 iOS 13。

ViewWillDisappear, ViewDidDisappear,ViewWillAppear并且 ViewDidAppear不会在 iOS 13 上的演示视图控制器上被调用,该控制器使用不覆盖整个屏幕的新模式演示。

学分将归于 Arek Holko。他真的拯救了我的一天。

在此处输入图像描述

于 2019-06-14T13:54:42.150 回答
9

我只是有同样的问题。在我的应用程序中,我有 2 个导航控制器并在每个控制器中推送相同的视图控制器在一种情况下工作,而不是在另一种情况下工作。我的意思是,当在第一个中推送完全相同的视图控制器时UINavigationControllerviewWillAppear会调用但在第二个导航控制器中推送时不会调用。

然后我遇到了这篇文章UINavigationController 应该调用 viewWillAppear/viewWillDisappear 方法

并意识到我的第二个导航控制器确实重新定义了viewWillAppear. 筛选代码显示我没有打电话

[super viewWillAppear:animated];

我添加了它并且它有效!

文档说:

如果你重写了这个方法,你必须在你的实现中调用 super 。

于 2010-01-13T14:46:52.903 回答
5

我一直在使用导航控制器。当我想下降到另一个级别的数据或显示我的自定义视图时,我使用以下内容:

[self.navigationController pushViewController:<view> animated:<BOOL>];

当我这样做时,我确实得到了viewWillAppear触发功能。我想这符合“间接”的条件,因为我addSubView自己没有调用实际方法。我不知道这是否 100% 适用于您的应用程序,因为我无法判断您是否使用导航控制器,但也许它会提供线索。

于 2008-09-25T19:24:01.590 回答
4

首先,标签栏应该在根级别,即添加到窗口中,如Apple文档中所述。这是正确行为的关键。

其次,您可以使用UITabBarDelegate/UINavigationBarDelegate手动转发通知,但我发现要让整个视图调用层次结构正常工作,我所要做的就是手动调用

[tabBarController viewWillAppear:NO];
[tabBarController viewDidAppear:NO];

[navBarController viewWillAppear:NO];
[navBarController viewDidAppear:NO];

.. 在相应控制器上设置视图控制器之前仅一次(分配后)。从那时起,它在其子视图控制器上正确地调用了这些方法。

我的层次结构是这样的:

window
    UITabBarController (subclass of)
        UIViewController (subclass of) // <-- manually calls [navController viewWill/DidAppear
            UINavigationController (subclass of)
                UIViewController (subclass of) // <-- still receives viewWill/Did..etc all the way down from a tab switch at the top of the chain without needing to use ANY delegate methods

只需第一次在选项卡/导航控制器上调用上述方法即可确保正确转发所有事件。它阻止了我需要从UINavigationBarDelegate/UITabBarControllerDelegate方法手动调用它们。

旁注:奇怪的是,当它不起作用时,私有方法

- (void)transitionFromViewController:(UIViewController*)aFromViewController toViewController:(UIViewController*)aToViewController 

..您可以从工作实现的调用堆栈中看到,通常调用viewWill/Did..方法,但直到我执行上述操作(即使它被调用)才调用。

我认为它UITabBarController处于窗口级别非常重要,并且文件似乎支持这一点。

希望这很清楚(ish),很高兴回答进一步的问题。

于 2011-07-26T17:10:55.653 回答
3

通过调用“直接”添加视图[view addSubview:subview]。通过交换子视图的选项卡栏或导航栏等方法“间接”添加视图。

任何时候你打电话[view addSubview:subviewController.view],你都应该打电话[subviewController viewWillAppear:NO](或者是,视你的情况而定)。

当我为游戏中的子屏幕实现自己的自定义根视图管理系统时,我遇到了这个问题。手动添加对 viewWillAppear 的调用解决了我的问题。

于 2010-11-22T03:21:13.770 回答
3

由于没有答案被接受并且人们(就像我一样)登陆这里,我给出了我的变化。虽然我不确定这是最初的问题。当导航控制器作为子视图添加到另一个视图时,您必须自己调用 viewWillAppear/Dissappear 等方法,如下所示:

- (void) viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [subNavCntlr viewWillAppear:animated];
}

- (void) viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    [subNavCntlr viewWillDisappear:animated];
}

只是为了使示例完整。此代码出现在我的 ViewController 中,我在其中创建导航控制器并将其添加到放置在视图上的视图中。

- (void)viewDidLoad {

    // This is the root View Controller
    rootTable *rootTableController = [[rootTable alloc]
                 initWithStyle:UITableViewStyleGrouped];

    subNavCntlr = [[UINavigationController alloc]   
                  initWithRootViewController:rootTableController];

    [rootTableController release];

    subNavCntlr.view.frame = subNavContainer.bounds;

    [subNavContainer addSubview:subNavCntlr.view];

    [super viewDidLoad];
}

.h 看起来像这样

@interface navTestViewController : UIViewController <UINavigationControllerDelegate> {
    IBOutlet UIView *subNavContainer;
    UINavigationController *subNavCntlr;
}

@end

在 nib 文件中,我有视图,在此视图下方,我有一个标签,一个图像和一个容器(另一个视图),我将控制器放入其中。这是它的外观。我不得不打乱一些东西,因为这是为客户工作的。

替代文字

于 2010-12-04T20:53:42.580 回答
3

正确的方法是使用 UIViewController 包含 API。

- (void)viewDidLoad {
     [super viewDidLoad];
     // Do any additional setup after loading the view.
     UIViewController *viewController = ...;
     [self addChildViewController:viewController];
     [self.view addSubview:viewController.view];
     [viewController didMoveToParentViewController:self];
}
于 2015-07-28T16:47:06.977 回答
2

一个非常常见的错误如下。您有一个视图,UIView* a和另一个视图,UIView* b。您将 b 添加到 a 作为子视图。如果你尝试在 b 中调用 viewWillAppear,它永远不会被触发,因为它是 a 的子视图

于 2013-12-05T14:05:07.733 回答
2

我将此代码用于推送和弹出视图控制器:

推:

[self.navigationController pushViewController:detaiViewController animated:YES];
[detailNewsViewController viewWillAppear:YES];

流行音乐:

[[self.navigationController popViewControllerAnimated:YES] viewWillAppear:YES];

..它对我来说很好。

于 2012-06-22T07:57:17.090 回答
2

iOS 13 将我的应用程序放在了此处。如果您注意到从 iOS 13 开始的行为变化,只需在推送之前设置以下内容:

yourVC.modalPresentationStyle = UIModalPresentationFullScreen;

您可能还需要在属性检查器的 .storyboard 中设置它(将 Presentation 设置为 Full Screen)。

这将使您的应用程序的行为与之前的 iOS 版本一样。

于 2019-10-16T14:49:55.727 回答
1

I'm not 100% sure on this, but I think that adding a view to the view hierarchy directly means calling -addSubview: on the view controller's view (e.g., [viewController.view addSubview:anotherViewController.view]) instead of pushing a new view controller onto the navigation controller's stack.

于 2008-10-07T15:51:46.657 回答
1
[self.navigationController setDelegate:self];

将委托设置为根视图控制器。

于 2010-11-23T13:36:12.533 回答
1

我认为他们“直接”的意思是通过与 xcode“导航应用程序”模板相同的方式连接事物,它将 UINavigationController 设置为应用程序 UIWindow 的唯一子视图。

使用该模板是我能够在 UINavigationController 中的这些控制器的推送/弹出时在对象 ViewControllers 上调用 Will/Did/Appear/Disappear 方法的唯一方法。这里的答案中没有其他解决方案对我有用,包括在 RootController 中实现它们并将它们传递给(子)NavigationController。这些功能(将/已/出现/消失)仅在显示/隐藏顶级 VC、我的“登录”和导航 VC,而不是导航控制器中的子 VC 时在我的 RootController 中调用,所以我没有机会将它们“传递给”导航 VC。

我最终使用 UINavigationController 的委托功能来查找在我的应用程序中需要后续功能的特定转换,并且可以正常工作,但它需要更多的工作才能让消失和出现功能“模拟”。

同样,在今天将我的头撞到这个问题几个小时之后,让它工作也是一个原则问题。任何使用自定义 RootController 和子导航 VC 的工作代码片段将不胜感激。

于 2010-06-15T15:50:25.783 回答
1

万一这对任何人都有帮助。我有一个类似的问题,我ViewWillAppear没有在UITableViewController. 在玩了很多之后,我意识到问题UINavigationController在于控制我UITableView的不在根视图上。一旦我解决了这个问题,它现在就像一个冠军一样工作。

于 2010-11-19T20:18:26.057 回答
1

我认为添加子视图并不一定意味着视图会出现,因此不会自动调用类的方法

于 2009-05-25T06:05:42.133 回答
1

我自己也遇到了这个问题,我花了 3 个完整小时(其中 2 个谷歌搜索)来解决它。

原来有帮助的是简单地从设备/模拟器中删除应用程序,清理然后再次运行

希望有帮助

于 2013-09-21T18:18:19.980 回答
1

对于斯威夫特。首先在 viewWillAppear 中创建协议来调用你想调用的东西

protocol MyViewWillAppearProtocol{func myViewWillAppear()}

二、创建类

class ForceUpdateOnViewAppear: NSObject, UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool){
    if let updatedCntllr: MyViewWillAppearProtocol = viewController as? MyViewWillAppearProtocol{
        updatedCntllr.myViewWillAppear()
    }
}

}

第三,使 ForceUpdateOnViewAppear 的实例成为有权访问 Navigation Controller 并且只要 Navigation Controller 存在就存在的适当类的成员。例如,它可能是导航控制器的根视图控制器或创建或呈现它的类。然后尽早将 ForceUpdateOnViewAppear 的实例分配给 Navigation Controller 委托属性。

于 2018-12-20T06:03:55.793 回答
0

就我而言,问题在于自定义过渡动画。设置时modalPresentationStyle = .custom viewWillAppear未调用

在自定义过渡动画类中需要调用方法: beginAppearanceTransitionendAppearanceTransition

于 2018-10-23T08:23:35.610 回答
0

我的问题是 viewWillAppear 在从 segue 展开时没有被调用。答案是在视图控制器的展开序列中调用 viewWillAppear(true)

@IBAction func unwind(for unwindSegue: UIStoryboardSegue, ViewController laterVC: Any) {

   viewWillAppear(true)
}
于 2020-04-15T07:01:37.203 回答
0

我创建了一个类来解决这个问题。只需将其设置为导航控制器的委托,并在视图控制器中实现简单的一两个方法 - 当视图即将显示或已通过 NavigationController 显示时将被调用

这是显示代码的 GIST

于 2019-02-21T10:31:15.317 回答
0

就我而言,这只是 ios 12.1 模拟器上的一个奇怪错误。在真机上启动后消失。

于 2019-01-29T19:25:52.403 回答
0

ViewWillAppear 是 UIViewController 类的重写方法,因此添加子视图不会调用 viewWillAppear,但是当您从 viewController 呈现、push、pop、show、setFront 或 popToRootViewController 时,将调用呈现的 viewController 的 viewWillAppear。

于 2019-02-21T10:52:17.690 回答
-2

我不确定这与我解决的问题相同。
在某些情况下,方法不会以正常方式执行,例如“[self methodOne]”。

尝试

- (void)viewWillAppear:(BOOL)animated
{
    [self performSelector:@selector(methodOne) 
           withObject:nil afterDelay:0];
}
于 2011-11-16T19:35:51.517 回答
-3

任何时候都应该只有 1 个 UIViewController 处于活动状态。您想要操作的任何子视图都应该正是那个 - subVIEWS - 即 UIView。

我使用一种简单的技术来管理我的视图层次结构,并且自从我开始以这种方式做事以来还没有遇到问题。有2个关键点:

  • 应该使用单个 UIViewController 来管理应用程序的“屏幕价值”
  • 使用 UINavigationController 更改视图

“屏幕的价值”是什么意思?故意有点模糊,但通常它是您应用程序的一个功能或部分。如果您有几个屏幕具有相同的背景图像但不同的覆盖/弹出窗口等,那应该是 1 个视图控制器和几个子视图。你永远不应该发现自己在使用 2 个视图控制器。请注意,如果您希望屏幕的某些区域显示在多个视图控制器中,您仍然可以在一个视图控制器中实例化 UIView 并将其添加为另一个视图控制器的子视图。

至于 UINavigationController - 这是你最好的朋友!关闭导航栏并将动画指定为 NO,您就有了一种按需切换屏幕的绝佳方式。如果视图控制器在层次结构中,您可以推送和弹出视图控制器,或者您可以准备视图控制器数组(包括包含单个 VC 的数组)并使用 setViewControllers 将其设置为视图堆栈。这使您可以完全自由地更改 VC,同时获得在 Apple 预期模型中工作和正确触发所有事件等的所有优势。

每次启动应用程序时,我都会这样做:

  • 从基于窗口的应用程序开始
  • 添加一个 UINavigationController 作为窗口的 rootViewController
  • 添加任何我想要我的第一个 UIViewController 作为导航控制器的 rootViewController

(注意从基于窗口开始只是个人喜好 - 我喜欢自己构建东西,所以我确切地知道它们是如何构建的。它应该与基于视图的模板一起工作)

所有事件都正确触发,基本上生活是美好的。然后,您可以将所有时间都花在编写应用程序的重要部分上,而不是试图手动修改视图层次结构。

于 2011-09-17T11:05:02.700 回答