Ios – Proper use of loadView and viewDidLoad with UIViewController without nibs/xibs

cocoa-touchiosuiviewcontroller

When I program without a nib, I am under the impression that I need to call loadView to initialize my view, like this:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nil bundle:nil];
    if (self) {
        // Custom initialization
        [self loadView];
    }
    return self;
}

(I have set nibNameOrNil = nil, since there is not nib.)

Then, I set up the view, like this:

- (void) loadView {
    self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 367)];
    [self viewDidLoad];
}

This is do everything else in viewDidLoad.

I'm still unsure if I am supposed to make the calls to loadView and viewDidLoad in this way. They are not getting called automatically.

What is confusing is in the documentation for the UIViewController class reference:

loadView Discussion

You should never call this method directly. The view controller calls
this method when the view property is requested but is currently nil.
If you create your views manually, you must override this method and
use it to create your views.
If you use Interface Builder to create
your views and initialize the view controller—that is, you initialize
the view using the initWithNibName:bundle: method, set the nibName and
nibBundle properties directly, or create both your views and view
controller in Interface Builder—then you must not override this
method.

So, I don't understand how loadView gets called if I should never call it directly.

The default implementation of this method looks for valid nib
information and uses that information to load the associated nib file.
If no nib information is specified, the default implementation creates
a plain UIView object and makes it the main view.

I don't understand how that works — the creation of a pain UIView.

If you override this method in order to create your views manually,
you should do so and assign the root view of your hierarchy to the
view property. (The views you create should be unique instances and
should not be shared with any other view controller object.) Your
custom implementation of this method should not call super.

If you want to perform any additional initialization of your views, do
so in the viewDidLoad method. In iOS 3.0 and later, you should also
override the viewDidUnload method to release any references to the
view or its contents.

Okay, so far it doesn't say how viewDidLoad is called. So for viewDidLoad:

viewDidLoad Discussion

This method is called after the view controller has loaded its
associated views into memory. This method is called regardless of
whether the views were stored in a nib file or created
programmatically in the loadView method. This method is most commonly
used to perform additional initialization steps on views that are
loaded from nib files.

Called by what?

Since These methods are not getting called automatically in my code, I am left to think that I have to call them myself. But I still don't get a clear understanding form the documentation that this is the right thing to do.

Best Answer

As the documentation says, you should not call these methods yourself.

How this is meant to work is that UIKit's code will call your loadView method if and when it actually needs to present that controller's view hierarchy on screen.

Specifically, if any code attempts to read your view property on your view controller, and that property is nil, then UIViewController's code will call the loadView method. If you don't implement loadView yourself, then the default implementation will fall back to attempting to load the view hierarchy from the nib.

This all happens automatically when you attempt to present your view controller and the view is nil. (This can happen multiple times while your app is running if your view controller has to unload its view in response to memory pressure, another behavior you get for 'free' and that you don't want to call yourself)

If these methods are not being called in your view controller, then there must be something wrong with how you are presenting this view controller, which is preventing the framework code in UIViewController from calling these methods. Post that code and someone can help you figure out that bug.

Before you attempt to fix that bug, though, you should remove these from your code:

[self loadView]
[self viewDidLoad]

And then in your own implementation of viewDidLoad, you will want to call the superclass' implementation with [super viewDidLoad]

Remember that in loadView your only responsibility to set self.view to some valid view. That's it. UIKit code will then see that and call viewDidLoad for you.

Hope that helps.