希望了解iOS UIViewController的生命周期


299

您能解释一下管理UIViewController生命周期的正确方法吗?

我特别想知道如何使用InitializeViewDidLoadViewWillAppearViewDidAppearViewWillDisappearViewDidDisappearViewDidUnloadDispose在单触摸方法一UIViewController类。


是否有OSX ViewController和WindowController的某些信息或链接?请分享。
Anoop Vaidya

Answers:


410

当您加载/显示/隐藏视图控制器时,iOS将在适当的时候自动调用所有这些命令。重要的是要注意,这些方法是附属于自身的,UIViewController而不是附属于UIView自身的。仅使用,您将不会获得任何这些功能UIView

有一个关于苹果公司的网站上丰富的文档在这里。简单地输入:

  • ViewDidLoad-在创建类并从xib加载时调用。非常适合初始设置和一次性工作。

  • ViewWillAppear-在视图出现之前立即调用,非常适合隐藏/显示字段或您希望在视图可见之前每次进行的任何操作。因为您可能在视图之间来回切换,所以每次视图将出现在屏幕上时都会调用此方法。

  • ViewDidAppear -在视图出现后调用-是启动动画或从API加载外部数据的好地方。

  • ViewWillDisappear/ DidDisappear-相同的思路ViewWillAppear/ ViewDidAppear

  • ViewDidUnload/ ViewDidDispose-在Objective-C中,您可以在此处进行清理和发布,但这是自动处理的,因此您在这里实际上不需要做很多事情。


86
该文本有些误导,因为不应将ViewDidLoad用于一次性工作。如果由于内存不足而卸载视图,然后再次加载,则可能会多次调用它。
里奇·海尔格森

4
创建/初始化视图控制器时,实际上不会调用ViewDidLoad。这是您第一次执行与视图控制器的视图相关的任何视图。就像将其添加为子视图,设置框架等。从笔尖加载时也当然会调用它。
杰森·格兰德利

3
ViewDidAppear-在视图出现后调用-是启动动画或从API加载外部数据的好地方。为什么是开始加载数据的好地方?为什么不使用viewDidLoad?
安东·奇金

1
那么关于loadView方法,如果第一次调用某个笔尖时是否在viewDidLoad之前将其加载到内存中,该方法如何。
iHulk

@chakrit这是一个好点-viewDidAppear是刷新数据的好地方(如果需要)。我不同意KVO,因为它会导致用户从未真正查看过的视图发生不必要的刷新。
安东·奇金

409

更新:ViewDidUnload在iOS 6中已弃用,因此相应地更新了答案。

UIViewController生命周期的示意图如下:

视图控制器的生命周期,如图所示

使用Xamarin Native / Mono Touch的优势在于它使用了本机API,因此它遵循与Apple文档中相同的ViewController生命周期。


17
此流程图上的viewWillLayoutSubviews和viewDidLayoutSubviews在哪里?
Max_Power89 2014年

7
该图不准确。自iOS6起不推荐使用viewDidUnload:stackoverflow.com/questions/12509102/…–
occulus

2
这确实是错误的。随着时间的流逝,SO上一个完全错误的答案的另一个例子。计算是高度静态的。
Fattie

186

这是针对最新的iOS版本(由Xcode 9.3,Swift 4.1修改)。以下是构成UIViewController完整生命周期的所有阶段。

  • loadView()

  • loadViewIfNeeded()

  • viewDidLoad()

  • viewWillAppear(_ animated: Bool)

  • viewWillLayoutSubviews()

  • viewDidLayoutSubviews()

  • viewDidAppear(_ animated: Bool)

  • viewWillDisappear(_ animated: Bool)

  • viewDidDisappear(_ animated: Bool)

让我解释所有这些阶段。

1。 loadView

此事件创建/加载控制器管理的视图。它可以从关联的nib文件加载,UIView如果找到null,则为空。这使它成为以编程方式在代码中创建视图的好地方。

如果子类不使用笔尖,则应在此处创建其自定义视图层次结构。永远不要直接调用。仅当以编程方式创建视图并将根视图分配给view属性时,才重写此方法。重写loadView时不要调用super方法。

2。 loadViewIfNeeded

如果viewController尚未设置当前视图,则此方法将加载视图,但请记住,仅在iOS> = 9.0中可用。因此,如果您支持iOS <9.0,那么不要指望它出现。

加载视图控制器的视图(如果尚未设置)。

3。 viewDidLoad

viewDidLoad仅当创建视图并将其加载到内存中时才调用该事件,但尚未定义视图的边界。这是初始化视图控制器将要使用的对象的好地方。

加载视图后调用。对于用代码创建的视图控制器,该命令位于-loadView之后。对于从笔尖未存档的视图控制器,这是在设置视图之后。

4。 viewWillAppear

viewController每当视图出现在屏幕上时,此事件都会通知。在此步骤中,视图具有已定义的边界,但未设置方向。

在视图将变为可见时调用。默认不执行任何操作。

5, viewWillLayoutSubviews

这是最终确定界限的生命周期的第一步。如果您没有使用约束或自动版式,则可能要在此处更新子视图。仅在iOS> = 5.0中可用。因此,如果您支持iOS <5.0,那么不要期望它出现在图片中。

在调用视图控制器的视图的layoutSubviews方法之前调用。子类可以根据需要实现。默认为nop。

6。 viewDidLayoutSubviews

此事件通知视图控制器子视图已设置。在设置子视图之后,可以对子视图进行任何更改。仅在iOS> = 5.0中可用。因此,如果您支持iOS <5.0,那么不要期望它出现在图片中。

在调用视图控制器的视图的layoutSubviews方法之后立即调用。子类可以根据需要实现。默认为nop。

7。 viewDidAppear

viewDidAppear视图显示在屏幕上后,事件触发。这使其成为从后端服务或数据库获取数据的好地方。

当视图完全过渡到屏幕上时调用。默认不执行任何操作

8。 viewWillDisappear

viewWillDisappear当所呈现的视图viewController即将消失,解散,掩盖或隐藏在其他对象后面时,将触发该事件viewController。在这里,您可以限制网络调用,使计时器无效或释放与其绑定的对象viewController

当视图被关闭,覆盖或其他隐藏时调用。

9。 viewDidDisappear

这是生命周期的最后一步,任何人都可以解决,因为呈现的视图viewController消失,消失,覆盖或隐藏之后,此事件才会触发。

在视图被撤消,覆盖或其他隐藏之后调用。默认不执行任何操作

现在,按照Apple的方法,在实现此方法时,您应该记住要调用super该特定方法的实现。

如果您将UIViewController子类化,则即使您未使用NIB,也必须调用此方法的超级实现。(为方便起见,默认的init方法将为您执行此操作,并为这两个方法参数都指定nil。)在指定的NIB中,File的Owner代理应将其类设置为您的视图控制器子类,并带有view出口连接到主视图。如果您使用nil nib名称调用此方法,则此类的-loadView方法将尝试加载名称与视图控制器的类相同的NIB。如果实际上不存在这样的NIB,则您必须在调用-setView:之前-view调用,或者重写该-loadView方法以编程方式设置视图。

希望这会有所帮助。谢谢。

更新 -正如@ThomasW在注释中指出的那样,viewWillLayoutSubviews并且viewDidLayoutSubviews在加载主视图的子视图时(例如,在加载表视图或集合视图的单元格时)也会在其他时间调用。

更新 -正如@Maria在评论中指出的,说明loadView已更新


6
viewWillLayoutSubviews并且viewDidLayoutSubviews还会在加载主视图的子视图的其他时间调用,例如,在加载表视图或集合视图的单元格时。
ThomasW

这个答案有一个小小的误导:总是调用loadView(),当在IB中创建控制器的视图时,不应覆盖它。
玛丽亚

@Maria如果可以改善,请继续编辑答案。谢谢。
onCompletion '18 / 10/15

默认值对没有什么不妥viewWillAppear viewDidAppear viewDidDisappear。您必须在某个时候叫超级。
米克,

47

iOS 10,11 (Swift 3.1,Swift 4.0)

UIViewControllerUIKit开发商,

1. loadView()

如果子类不使用nib,则应在此处创建其自定义视图层次结构。永远不要直接调用。

2. loadViewIfNeeded()

加载视图控制器的视图(如果尚未设置)。

3. viewDidLoad()

加载视图后调用。对于用代码创建的视图控制器,该命令位于-loadView之后。对于从笔尖未存档的视图控制器,这是在设置视图之后。

4. viewWillAppear(_动画:布尔)

在视图将变为可见时调用。默认不执行任何操作

5. viewWillLayoutSubviews()

在调用视图控制器的视图的layoutSubviews方法之前调用。子类可以根据需要实现。默认不执行任何操作。

6. viewDidLayoutSubviews()

在调用视图控制器的视图的layoutSubviews方法之后立即调用。子类可以根据需要实现。默认不执行任何操作。

7. viewDidAppear(_动画:布尔)

当视图完全过渡到屏幕上时调用。默认不执行任何操作

8. viewWillDisappear(_动画:布尔)

当视图被关闭,覆盖或其他隐藏时调用。默认不执行任何操作

9. viewDidDisappear(_动画:Bool

在视图被撤消,覆盖或其他隐藏之后调用。默认不执行任何操作

10. viewWillTransition(大小:CGSize,协调器:UIViewControllerTransitionCoordinator)

当视图正在过渡时调用。

11. willMove(toParentViewController parent:UIViewController?)

12. didMove(toParentViewController parent:UIViewController?)

在子控制器之间转换时,这两种方法是公共的,供容器子类调用。如果它们被覆盖,则覆盖应确保调用超级。

当从其父级中移除子级时,这两种方法的父级参数均为nil。否则,它等于新的父视图控制器。

13. didReceiveMemoryWarning()

当父应用程序收到内存警告时调用。在iOS 6.0上,默认情况下将不再清除视图。


2
确实,stackoverflow不会清除整个线程中所有错误和不完整的答案,这真是很遗憾。就方法调用而言,您的答案似乎是完整的,因此,我将假定您的回答正确并使用该方法。
Logicsaurus Rex

nib在下面提到的是什么loadView
Petrus Theron '18

2
@LogicsaurusRex我同意。以同样的方式将SO标记为重复或受保护的问题,我认为它应该能够将答案标记为过时过时
rmp251

上面的第5点是错误的。viewWillLayoutSubviews()在ViewController的视图对象调用其layoutSubviews()方法之前被调用
williamukoh 18/12/7

28

从iOS 6开始。新图如下:

在此处输入图片说明


1
将该视图称为“ A”。考虑当“ A”消失时出现的第二个视图“ B”。“ B.viewWillAppear”在“ A.viewDidDisappear”之前还是之后?在任何情况下,这两个顺序都会发生变化吗?
ToolmakerSteve

好像新视图的(B)willApear将在消失之前被调用。对于第二个问题。需要一些时间来研究它。
萨德2016年

21

让我们集中讨论负责UIViewController生命周期的方法:

  • 创建:

    - (void)init

    - (void)initWithNibName:

  • 视图创建:

    - (BOOL)isViewLoaded

    - (void)loadView

    - (void)viewDidLoad

    - (UIView *)initWithFrame:(CGRect)frame

    - (UIView *)initWithCoder:(NSCoder *)coder

  • 视图状态更改的处理:

    - (void)viewDidLoad

    - (void)viewWillAppear:(BOOL)animated

    - (void)viewDidAppear:(BOOL)animated

    - (void)viewWillDisappear:(BOOL)animated

    - (void)viewDidDisappear:(BOOL)animated

    - (void)viewDidUnload

  • 内存警告处理:

    - (void)didReceiveMemoryWarning

  • 解除分配

    - (void)viewDidUnload

    - (void)dealloc

UIViewController的生命周期图

有关更多信息,请参见UIViewController类参考


19

的方法viewWillLayoutSubviewsviewDidLayoutSubviews在图中未提及,但这些被称为之间viewWillAppearviewDidAppear。可以多次调用它们。


在加载主视图的子视图时(例如,在加载表视图或集合视图的单元格时)也会在其他时间调用它们。
ThomasW

16

Haider的答案对于iOS 6之前的版本是正确的。但是,从iOS 6开始,从未调用过viewDidUnload和viewWillUnload。该文档的状态:“视图不再低内存情况下,因此这种方法不会被调用清除。”


我尝试在ViewWillDisappear,ViewDidDisappear,Dispose中设置断点。但是,当我使用PresentViewController()方法导航时,没有一个被调用。可能是什么原因 ?
Sreeraj 2014年

1
链接不起作用...那么在内存不足的情况下OS会做什么?
2015年

将它们保存到磁盘吗?
伊恩·沃伯顿

16

这里有很多过时和不完整的信息。仅适用于iOS 6及更高版本

  1. loadView[一个]
  2. viewDidLoad[一个]
  3. viewWillAppear
  4. viewWillLayoutSubviews 是第一次确定界限
  5. viewDidLayoutSubviews
  6. viewDidAppear
  7. * viewWillLayoutSubviews[b]
  8. * viewDidLayoutSubviews[b]

脚注:

(一) -如果你手动零出在你的看法didReceiveMemoryWarningloadViewviewDidLoad会被再次调用。也就是说,默认情况下loadViewviewDidLoad每个视图控制器实例仅被调用一次。

(b)可能被称为0次或多次。


1
viewWillLayoutSubviews并且viewDidLayoutSubviews还会在加载主视图的子视图的其他时间调用,例如,在加载表视图或集合视图的单元格时。
ThomasW


0

按照Apple的文档-开始开发iOS应用(快速)-使用View Controllers-了解View Controller生命周期

viewDidLoad()—在创建视图控制器的内容视图(其视图层次结构的顶部)并从情节提要中加载时调用。…使用此方法执行视图控制器所需的任何其他设置。

viewWillAppear()—在将视图控制器的内容视图添加到应用程序的视图层次结构之前调用。使用此方法触发在屏幕上显示内容视图之前需要进行的任何操作

viewDidAppear()—在将视图控制器的内容视图添加到应用程序的视图层次结构之后立即调用。使用此方法可以触发在屏幕上显示视图后立即进行的所有操作,例如获取数据或显示动画。

viewWillDisappear()—在将视图控制器的内容视图从应用程序的视图层次结构中删除之前调用。使用此方法可以执行清除任务,例如提交更改或退出第一响应者状态。

viewDidDisappear()—在从应用程序的视图层次结构中删除视图控制器的内容视图之后立即调用。使用此方法执行其他拆卸活动。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.