UIViewController诞生的过程是什么(哪个方法遵循哪个方法)?

时间:2011-02-24 16:38:24

标签: iphone uiviewcontroller

有许多方法可以覆盖,例如initWithNibname:awakeFromNibloadViewviewDidLoadviewDidAppear:layoutSubviews,我只是无法决定调用这些方法的顺序。

我只是“用心”覆盖其中一个。

任何详细的解释?

7 个答案:

答案 0 :(得分:173)

Cocoa view and viewController management幕后有很多事情要发生。

<强> 1。 viewController对象

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

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

viewControllers的指定初始值设定项为-initWithNibname:bundle:。如果指定一个nib,viewController可以从该nib自动加载其视图并连接您定义的任何IBOutlet(详见下文)。

<强> 2。加载和卸载视图

viewController将根据需要加载其视图。这通常在第一次调用-view方法时发生,并且可能在程序中的任何时间发生,具体取决于您初始化UI的方式。在程序的生命周期中,视图也可能被破坏并重新加载,这取决于您管理UI的方式。当viewController识别出其视图是必需的但尚未加载时,将调用-loadView方法。基本的消息流程如下:

view
  loadView
  viewDidLoad

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

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

view = nil
   viewDidUnload

<强> 2a上。以编程方式加载视图

如果您选择覆盖-loadView,则可以以任何方式创建视图,子视图,其他viewControllers以及这些对象之间的任何连接。当然,这意味着您还负责与您创建的对象相关的内存管理。如果您的子类重写-loadView,则应使用nilnibNamebundle初始化它。

<强> 2B。从笔尖加载视图

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

使用nib文件会让事情变得更棘手,因为幕后发生了很多事情。为加载nib文件时实例化的每个对象调用-awakeFromNib方法,并且无法保证nib文件中的其他对象在完成时已完全加载调用。

第3。显示视图

-viewWillAppear:-viewDidAppear:-viewWillDisappear:-viewDidDisappear:仅在屏幕上显示或隐藏视图时调用,尤其是在从一个视图到另一个视图的动画转换期间。在程序的生命周期中,可以多次调用这些方法,因为视图在导航方案中被换入和换出。

<强> 4。查看布局

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

<强> 5。全部放在一起

由于复杂性,此过程有许多不同的方式,但正常的时间线可能如下所示:

-[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]
...

答案 1 :(得分:36)

我最近重新访问了这个并创建了一个测试项目:https://github.com/Janek2004/ViewControllerTest

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

  1. - [ViewController initWithCoder:] 从nib或storyboard中取消归档数据
  2. - [ViewController awakeFromNib] 从Interface Builder档案或笔尖加载后,准备接收器以进行维修 文件。
  3. - [ViewController loadView] 您永远不应该直接调用此方法。视图控制器在其视图时调用此方法 请求财产,但目前是零。此方法加载或 创建一个视图并将其分配给视图属性。
  4. - [ViewController viewDidLoad] 在视图控制器将其视图层次结构加载到内存后调用此方法。
  5. - [ViewController viewWillAppear:] 在接收者的视图即将添加到视图层次结构之前和之前调用此方法 任何动画都配置为显示视图。
  6. - [ViewController viewWillLayoutSubviews] 调用以通知视图控制器其视图即将布局其子视图。当 视图的边界发生变化,视图会调整其子视图的位置。 您的视图控制器可以覆盖此方法以进行更改 该视图列出了其子视图。
  7. - [ViewController viewDidLayoutSubviews] 被调用以通知视图控制器其视图刚刚布置了其子视图。当。。。的时候 视图控制器视图的边界更改,视图调整 其子视图的位置然后系统调用此方法。 但是,调用此方法并不表示该方法 已调整视图子视图的各个布局。每 subview负责调整自己的布局。
  8. - [ViewController viewDidAppear:] 通知视图控制器其视图已添加到视图层次结构中。您可以覆盖此方法 执行与展示视图相关的其他任务。

  9. - [ViewController viewWillDisappear:] 通知视图控制器其视图即将从视图层次结构中删除。 调用方法以响应从视图中删除的视图 层次结构。在实际删除视图之前调用此方法 在配置任何动画之前。通知视图 控制器,其视图已添加到视图层次结构中。您可以 覆盖此方法以执行与之关联的其他任务 提出观点。

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

答案 2 :(得分:9)

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

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

答案 3 :(得分:4)

来自Apple UIViewController文档的

  

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

从头到尾:

  1. initWithNibname
  2. loadView(手动加载)
  3. viewDidiLoad
  4. viewDidAppear
  5. 无法确定layoutSubviews的输入位置

答案 4 :(得分:1)

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

答案 5 :(得分:1)

-- This is related to view only:
-viewWillAppear:
-viewDidAppear: 
-viewWillDisappear: and 
-viewDidDisappear: 

are only called when the view is being displayed.

-viewController viewDidLoad
-viewController viewWillAppear
-viewController viewDidAppear

other methods

-viewController viewDidDisappear
-viewController viewWillDisappear 
-viewController viewDidUnload

答案 6 :(得分:0)

我要感谢e.James的精彩描述。我还不能对帖子发表评论,但为了快速直观地说明,请参阅View Controller编程指南中的this flow chart。我意识到这是偏离主题的,但对于graph of the app launch sequence,请参阅iOS应用程序编程指南。