57

There are many methods to override, like initWithNibname:, awakeFromNib, loadView, viewDidLoad, viewDidAppear:, layoutSubviews, and I just cannot decide in which order gets these method called.

I just override one of them "by heart".

Any detailed explaination?

Elijah
  • 8,381
  • 2
  • 55
  • 49
Geri Borbás
  • 15,810
  • 18
  • 109
  • 172

7 Answers7

175

There is a lot going on behind the scenes with Cocoa view and viewController management.

1. The viewController object

At its most basic, a viewController is a generic controller object. When it is first allocated an initialized, it has no view object associated with it. The view is only instantiated when (and if) it is required. So, without considering the view, the lifecycle of a viewController is the same as any other object:

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

The designated initializer for viewControllers is -initWithNibname:bundle:. If you specify a nib, the viewController can automagically load its view from that nib and connect any IBOutlets that you have defined (see below for more details).

2. Loading and unloading the view

A viewController will load its view as required. This usually happens when the -view method is called for the first time, and can happen at any time in your program, depending on how you initialize your UI. The view may also be destroyed and reloaded several times during the lifetime of your program, agan depending on how you manage your UI. When the viewController has identified that its view is required but not yet loaded, the -loadView method will be called. The basic message flow goes something like this:

view
  loadView
  viewDidLoad

Note that if you override the -view method, -loadView and viewDidLoad will not be called automatically. If you override -loadView, you must set the viewController's view property. Otherwise, the next call to -view will trigger the loading process again.

The view may also be unloaded at any time during the lifetime of your program simply by setting the view property to nil. The default implementation of -didReceiveMemoryWarning will do this automatically, as long as the view does not have a superview (i.e. if it is not currently part of the active view heirarchy). The message flow goes as follows:

view = nil
   viewDidUnload

2a. Loading the view programmatically

If you choose to override -loadView, you can create a view, subviews, other viewControllers, and any connections between these objects in any way you please. Of course, this means that you are also responsible for memory management with respect to the objects that you create. If your subclass overrides -loadView, it should be initialized using nil for both nibName and bundle.

2b. Loading the view from a nib

If you use a nib file, the default implementation of -loadView will automatically open that nib file, instantiate its objects, add any connections between them, and take care of the memory management for you.

Things get a little more tricky with nib files, since so much happens behind the scenes. The -awakeFromNib method is called for every object that is instantiated when a nib file is loaded, and there is no guarantee that the other objects in the nib file will have been fully loaded when it is called.

3. Displaying views

-viewWillAppear:, -viewDidAppear:, -viewWillDisappear: and -viewDidDisappear: are only called when the view is being displayed or hidden on-screen, especially during animated transistions from one view to another. These methods may be called many times during the lifetime of your program, as views are swapped in and out in your navigation scheme.

4. View layout

The -layoutSubviews method is not part of UIViewController. It is called for UIView objects when their bounds have been changed. If you use a custom UIView subclass in your program, this method can be used to do custom subview layout instead of relying on Cocoa's default autoresizing methods.

5. Putting it all together

Because of the complexity, there are many different ways for this process to occur, but a normal timeline could look something like this:

-[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]
...
e.James
  • 116,942
  • 41
  • 177
  • 214
  • 8
    It is also worth noting that initWithCoder will be called instead of initWithNibName if you view controllers are loaded from a .xib file (i.e. your view controller is defined in your storyboard). – Mick Apr 12 '13 at 17:33
  • @e.James, I recently added QLPreviewController to one of my projects and noticed that it causes viewDidLoad to be called in my main VC when it is dismissed. I'm not doing anything to save my view, yet it comes back as if it had never been taken down, except that viewDidLoad is called. Can you weigh in on this? – Victor Engel Jul 04 '13 at 03:56
  • @Victor Engel: I haven't done much Cocoa development in the last couple of years. I would be guessing at this point, so you would be better off asking a new question! – e.James Jul 09 '13 at 05:17
  • Actually, I think it was viewWillAppear that was being called. In any case, I concluded it was because the preview function is actually a whole other app, so that puts the caller into the background. Maybe someone else can correct me if I'm wrong about this. In any case, I got my app working the way I wanted it. – Victor Engel Jul 10 '13 at 06:56
  • 2
    Concerning point 4, it is possible (in iOS 5+) to override `- (void)viewWillLayoutSubviews` and `- (void)viewDidLayoutSubviews` on `UIViewController` to insert logic at this point in the lifecycle. – devios1 Oct 11 '13 at 18:12
38

I revisited this lately and created a test project: https://github.com/Janek2004/ViewControllerTest

Run the project on the iOS simulator to see the order of execution of the UIViewController subclass methods. Order might be different whenever we use Nib file instead of the storyboard or load view controller programmatically.

  1. -[ViewController initWithCoder:] Unarchive data from nib or storyboard
  2. -[ViewController awakeFromNib] Prepares the receiver for service after it has been loaded from an Interface Builder archive, or nib file.
  3. -[ViewController loadView] You should never call this method directly. The view controller calls this method when its view property is requested but is currently nil. This method loads or creates a view and assigns it to the view property.
  4. -[ViewController viewDidLoad] This method is called after the view controller has loaded its view hierarchy into memory.
  5. -[ViewController viewWillAppear:] This method is called before the receiver’s view is about to be added to a view hierarchy and before any animations are configured for showing the view.
  6. -[ViewController viewWillLayoutSubviews] Called to notify the view controller that its view is about to layout its subviews.When a view’s bounds change, the view adjusts the position of its subviews. Your view controller can override this method to make changes before the view lays out its subviews.
  7. -[ViewController viewDidLayoutSubviews] Called to notify the view controller that its view has just laid out its subviews. When the bounds change for a view controller’s view, the view adjusts the positions of its subviews and then the system calls this method. However, this method being called does not indicate that the individual layouts of the view’s subviews have been adjusted. Each subview is responsible for adjusting its own layout.
  8. -[ViewController viewDidAppear:] Notifies the view controller that its view was added to a view hierarchy. You can override this method to perform additional tasks associated with presenting the view.

  9. -[ViewController viewWillDisappear:] Notifies the view controller that its view is about to be removed from a view hierarchy.This method is called in response to a view being removed from a view hierarchy. This method is called before the view is actually removed and before any animations are configured. Notifies the view controller that its view was added to a view hierarchy. You can override this method to perform additional tasks associated with presenting the view.

  10. -[ViewController viewDidDisappear:] Notifies the view controller that its view was removed from a view hierarchy.
Janusz Chudzynski
  • 2,700
  • 3
  • 33
  • 46
  • And another very important step is between 7 and 8, when -[View layoutSubviews] is called on the view and subviews of the ViewController. See my answer http://stackoverflow.com/a/27073676/173875 for more details on why that step is important for auto layout constrained layouts. – Elijah Nov 22 '14 at 03:28
10

Another key moment in the process is when layoutSubviews is called on any subviews. It is at this point, and not any sooner, that any constraints configured in storyboard have been applied. If you need to make any adjustments to subviews of a view, based on it's constrained coordinates, you have to do it in layoutSubviews. If you do it in viewDidLayoutSubviews, that will be too soon, as those subviews have not yet had their constraints applied (because as the documentation says "Each subview is responsible for adjusting its own layout".) And if you do it in viewDidAppear, that will be too late obviously, as the user will see your subviews change coordinates. So, the other vital step in the process is:

-viewController viewWillAppear
-viewController viewWillLayoutSubviews
-viewController viewDidLayoutSubviews
---> viewController.[any subview] layoutSubviews
-viewController viewDidAppear  
Elijah
  • 8,381
  • 2
  • 55
  • 49
4

from Apple UIViewController documentation:

When you define a new subclass of UIViewController, you must specify the views to be managed by the controller. There are two mutually exclusive ways to specify these views: manually or using a nib file. If you specify the views manually, you must implement the loadView method and use it to assign a root view object to the view property. If you specify views using a nib file, you must not override loadView but should instead create a nib file in Interface Builder and then initialize your view controller object using the initWithNibName:bundle: method. Creating views using a nib file is often simpler because you can use the Interface Builder application to create and configure your views graphically (as opposed to programmatically). Both techniques have the same end result, however, which is to create the appropriate set of views and expose them through the view property.

From the top of my head:

  1. initWithNibname
  2. loadView (load stuff manually)
  3. viewDidiLoad
  4. viewDidAppear

no clue where layoutSubviews enters

Blitz
  • 5,521
  • 3
  • 35
  • 53
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
Ram S
  • 793
  • 12
  • 21
1

I generally solve this question by putting an NSLog (or breakpoints) in all of these delegates, including the app launch delegate, and following the order in the debugger.

hotpaw2
  • 70,107
  • 14
  • 90
  • 153
  • Maybe, but threading can joke me... Maybe there is something "official" answer to this. I often resize frames for example when views are not "done" yet. I have problems with a view in a superview in a navigation stack in a tab bar controller in a main viewcontroller... I just cannot follow the process. – Geri Borbás Feb 24 '11 at 16:57
  • Threading should not be a problem, since all UI view and controller activity should take place in the single UI main thread. – hotpaw2 Jan 21 '16 at 02:06
0

I want to thank e.James for his excellent description. I cannot yet comment on a post, but for a quick visual illustration, refer to this flow chart in the View Controller programming guide. And I realize that this is off-topic, but for a graph of the app launch sequence, refer to the iOS Application Programming Guide.

Chris Conover
  • 8,889
  • 5
  • 52
  • 68