什么是一个UIViewController诞生的过程(后面哪个方法)?

有许多方法可以重写,比如initWithNibname:awakeFromNibloadViewviewDidLoadviewDidAppear:layoutSubviews ,而我无法决定以何种顺序获取这些方法。

我只是重复其中的一个“的心”。

任何详细的解释?

Cocoa视图和viewControllerpipe理幕后有很多事情要做。

1. viewController对象

在最基本的情况下,viewController是一个通用的控制器对象。 当它被初始化分配时,它没有与之关联的视图对象。 该视图仅在需要时(和如果)实例化。 所以,在不考虑视图的情况下,viewController的生命周期与任何其他对象相同:

 UIViewController * myVC = [[UIViewController alloc] initWith...]; ... [myVC release]; 

viewControllers的指定初始值设定项是-initWithNibname -initWithNibname:bundle: :。 如果指定了一个nib,viewController可以从该nib自动加载它的视图,并连接你定义的任何IBOutlets(参见下面的更多细节)。

2.加载和卸载视图

viewController将根据需要加载其视图。 这通常是在第一次调用-view方法时发生的,并且可能随时在您的程序中发生,具体取决于您初始化UI的方式。 视图也可能会在程序生命周期中被破坏和重新加载多次,具体取决于您如何pipe理UI。 当viewController已经确定它的视图是必需的,但尚未加载时, -loadView方法将被调用。 基本的消息stream程如下所示:

 view loadView viewDidLoad 

请注意,如果您重写-view方法,则不会自动调用-loadViewviewDidLoad 。 如果您重写-loadView ,则必须设置viewController的view属性。 否则,下一次对-view调用将再次触发加载过程。

视图也可以在程序生命周期的任何时候卸载,只需将view属性设置为nil-didReceiveMemoryWarning的默认实现将自动执行此操作,只要该视图没有-didReceiveMemoryWarning视图(即,如果它当前不是活动视图的图层的一部分)。 消息stream程如下:

 view = nil viewDidUnload 

2A。 以编程方式加载视图

如果您select覆盖-loadView ,则可以用任何方式创build视图,子视图,其他viewControllers以及这些对象之间的任何连接。 当然,这意味着你也要对你创build的对象负责内存pipe理。 如果你的子类重载-loadView ,它应该用nil来初始化nibNamebundle

2B。 从笔尖加载视图

如果使用nib文件, -loadView的默认实现将自动打开该nib文件,实例化其对象,在它们之间添加任何连接,并为您负责内存pipe理。

由于在幕后发生了很多事情,事情变得更加棘手。 对于在加载nib文件时实例化的每个对象都会调用-awakeFromNib方法,并且不能保证nib文件中的其他对象在调用时将被完全加载。

3.显示视图

-viewWillAppear: ,- -viewDidAppear: ,- -viewWillDisappear: and -viewDidDisappear:只在视图被显示或隐藏在屏幕上时被调用,特别是在从一个视图到另一个视图的animation转换期间。 在程序生命周期中,这些方法可能被调用很多次,因为视图在导航scheme中被换入和换出。

4.查看布局

-layoutSubviews方法不是 UIViewController一部分。 它的边界被改变时被调用UIView对象。 如果您在程序中使用自定义UIView子类,则可以使用此方法执行自定义子视图布局,而不是依赖Cocoa的默认自动调整方法。

把它放在一起

由于这个过程的复杂性,这个过程有很多种不同的方式,但是一个正常的时间表可能是这样的:

 -[viewController initWithNibname:Bundle:] -[viewController awakeFromNib] -[viewController loadView] -[view awakeFromNib] -[viewController viewDidLoad] -[viewController viewWillAppear] -[viewController viewDidAppear] ... -[viewController viewWillDisappear] // user navigated away -[viewController viewDidDisappear] ... -[viewController viewWillAppear] // user navigated back -[viewController viewDidAppear] ... -[viewController viewWillDisappear] // user navigated away -[viewController viewDidDisappear] ... -[viewController setView:nil] // memory warning, perhaps -[viewController viewDidUnload] ... -[viewController loadView] // user navigated back -[view awakeFromNib] -[viewController viewDidLoad] -[viewController viewWillAppear] -[viewController viewDidAppear] ... 

我最近重新研究这个,并创build了一个testing项目: https : //github.com/Janek2004/ViewControllerTest

在iOS模拟器上运行项目,查看UIViewController子类方法的执行顺序。 当我们使用Nib文件代替故事板或以编程方式加载视图控制器时,顺序可能会不同。

  1. – [ViewController initWithCoder:]从nib或storyboard解除数据存档
  2. – [ViewController awakeFromNib]从接口生成器存档或nib文件加载后,准备接收器以进行服务。
  3. – [ViewController loadView]你不应该直接调用这个方法。 视图控制器在请求其视图属性时调用此方法,但当前为零。 此方法加载或创build视图并将其分配给视图属性。
  4. – [ViewController viewDidLoad]在视图控制器将其视图层次加载到内存之后调用此方法。
  5. – [ViewController viewWillAppear:]在将接收者视图添加到视图层次结构之前,以及在configuration任何animation以显示视图之前调用此方法。
  6. – [ViewController viewWillLayoutSubviews]调用以通知视图控制器其视图即将布置其子视图。当视图的边界发生更改时,视图会调整子视图的位置。 你的视图控制器可以覆盖这个方法来进行更改之前,视图布局它的子视图。
  7. – [ViewController viewDidLayoutSubviews]调用来通知视图控制器,它的视图刚刚布置了它的子视图。 当视图控制器视图的边界发生变化时,视图会调整其子视图的位置,然后系统会调用此方法。 但是,调用此方法并不表示视图子视图的各个布局已被调整。 每个子视图负责调整自己的布局。
  8. – [ViewController viewDidAppear:]通知视图控制器其视图已添加到视图层次结构中。 您可以重写此方法以执行与呈现视图相关的其他任务。

  9. – [ViewController viewWillDisappear:]通知视图控制器其视图即将从视图层次结构中删除。调用此方法以响应从视图层次结构中删除的视图。 在视图被实际删除之前以及configuration任何animation之前调用此方法。 通知视图控制器其视图已添加到视图层次结构中。 您可以重写此方法以执行与呈现视图相关的其他任务。

  10. – [ViewController viewDidDisappear:]通知视图控制器其视图已从视图层次结构中删除。

在这个过程中的另一个关键时刻是在任何子视图上调用layoutSubviews。 在这一点上,而不是在早些时候,故事板中configuration的任何约束都已经被应用。 如果您需要根据视图的受限坐标对视图的子视图进行任何调整,则必须在layoutSubviews中执行此操作。 如果你在viewDidLayoutSubviews中做了这个,那就太早了,因为那些子视图还没有应用它们的约束(因为正如文档中所说的“每个子视图负责调整自己的布局”)。如果你在viewDidAppear中做了这个,这显然太迟了,因为用户会看到你的子视图改变坐标。 所以,这个过程的另一个重要步骤是:

 -viewController viewWillAppear -viewController viewWillLayoutSubviews -viewController viewDidLayoutSubviews ---> viewController.[any subview] layoutSubviews -viewController viewDidAppear 

来自Apple UIViewController文档:

在定义UIViewController的新子类时,必须指定要由控制器pipe理的视图。 有两种相互排斥的方式来指定这些视图:手动或使用nib文件。 如果您手动指定视图,则必须实现loadView方法并使用它将一个根视图对象分配给视图属性。 如果使用nib文件指定视图,则不能覆盖loadView,而应该在Interface Builder中创build一个nib文件,然后使用initWithNibName:bundle:方法初始化视图控制器对象。 使用nib文件创build视图通常更简单,因为您可以使用Interface Builder应用程序以graphics方式创build和configuration视图(而不是以编程方式)。 两种技术都有相同的最终结果,但是,这是创build适当的视图集,并通过视图属性公开它们。

从我的头顶上:

  1. initWithNibname
  2. loadView(手动加载东西)
  3. viewDidiLoad
  4. viewDidAppear

不知道layoutSubviews进入的地方

我通常通过在所有这些委托中放置一个NSLog(或断点)来解决这个问题,包括应用程序启动委托,以及在debugging器中的顺序。

 - 这只与查看有关:
 -viewWillAppear:
 -viewDidAppear: 
 -viewWillDisappear:和 
 -viewDidDisappear: 

只有在显示视图时才被调用。

 -viewController viewDidLoad
 -viewController viewWillAppear
 -viewController viewDidAppear

其他方法

 -viewController viewDidDisappear
 -viewController viewWillDisappear 
 -viewController viewDidUnload

我想感谢e.James对他的出色描述。 我还无法对post发表评论,但对于快速的视觉说明,请参阅View Controller编程指南中的stream程图 。 而且我意识到这是无关紧要的,但是对于应用程序启动顺序的graphics ,请参阅“iOS应用程序编程指南”。