Apple's docs do not say what the correct implementation is for loadView.
I've found that if you implement loadView like this:
- (void)loadView
{
self.view = [[UIView alloc] init];
}
...then you get different behaviour than if you don't implement it at all. In particular, in one 20-line project, I've found that viewWillAppear is called with a zero-size frame for self.view - unless you use Apple's default version of loadView.
Looking on Google, there are lots of "tutorials" that provide obviously-wrong loadView implementations - e.g. force-setting the size to (320,480) because the tutorial author "found that it works if I do this".
I'd like to know what the correct implementation should be.
NB: in my example above, I'm adding it to the view hierarchy inside AppDelegate like this:
[self.window addSubview:(UIViewController*).view];
I believe that in the presence of a UINavigationController or UITabBarController, Apple does some extra magic that - as a side-effect - causes a one-line loadView implementation to work OK. But I want to write it correctly, so that it always works!
NB: I've tried setting the autoresizing mask on the root view, but it doesn't change what happens:
- (void)loadView
{
self.view = [[UIView alloc] init];
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
First, there is no 'default' implementation of
loadView
...that method is specifically there for you to override. I do agree that Apple's docs can be a little unclear though. ButloadView
is called by default whenever the view of the navigation controller is accessed and no view exists (for example:UIView *view = viewController.view
). It can also be called manually. But in no situation willloadView
have the correct dimensions...that is, in fact, impossible.loadView
is called in order for the parent view controller to get the view in the first place so it can size it appropriately. Then once it gets the view it callsviewDidLoad
. This is the only code path they can use because views can load from theloadView
method or the nib and they must provide a place for additional setup when views are loaded from a nib. Finally, the parent controller will resize the view and callviewWillAppear
only when the view will actually appear. For example, if you push a controller on a navController that's off screen, it won't callviewWillAppear
until the navController itself is placed on screen. This is done because there's no point in running that code until the controller is actually visible. This is also why you can only ever get the correct dimension in theviewWillAppear
method.Now, you noticed that if you add a controller to a standard controller none of this stuff happens. This is because view controllers aren't really intended to contain other view controllers per say. Now in iOS 5, they explicitly support the use of Container View Controllers...which is essentially a view controller that IS designed to contain other view controllers. They added a few 'convenience' methods in iOS 5 to help with this but it's not strictly necessary. The jist of all this is: if you want to add one view controller to another, you will have to manually setup all the appropriate calls to the child view controller (all loading methods, rotation events, memory warning etc). In other words, you have to make your own container view controller. When you do, though, keep in mind what I said before about the code path. It's important that you call child controller methods in the same order Apple does or stuff won't work right.
Here's some links to info: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html -Scroll down to: Implementing a Container View Controller
Also here for the view controller life cycle, which will help you figure out which calls need to be made in which order: http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ViewLoadingandUnloading/ViewLoadingandUnloading.html#//apple_ref/doc/uid/TP40007457-CH10-SW1
I do recommend reading the entire View Controller Programming Guide....you can gleam a lot of information from there: http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/Introduction/Introduction.html#//apple_ref/doc/uid/TP40007457-CH1-SW1
Default implementation of
-loadView
creates the view or loads NIB. As far as I know, there is no way to know the final size of the view at time of creation in-loadView
. So the default view size is set toUIScreen.mainScreen.bounds
. This is because it may be difficult to work with zero frame view in-viewDidLoad
and other methods.Your one-line implementation may look like this:
You don't need to set the autoresizing mask, because you don't know in what context the view will be displayed. The caller is responsible to set you correct frame, autoresizing mask and similar properties.
Imagine this in a
UINavigationController
method:It is setting the correct frame, but your
-loadView
is called just before that-setFrame:
. So during-viewDidLoad
you have temporary non-zero frame, just to be able to setup subviews and internal autoresizing. After this, the correct frame is set to you and in-viewWillAppear:
you have final frame.