Ios – Correct loadView implementation

iosuiviewuiviewcontroller

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;
}

Best Answer

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 to UIScreen.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:

- (void)loadView {
    self.view = [[UIView alloc] initWithFrame:UIScreen.mainScreen.bounds];
}

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:

// we are pushing new VC, view is accessed for the first time
pushedVC.view.frame = CGRectMake(...);

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.

Related Topic