Proper Use of Loadview and Viewdidload with Uiviewcontroller Without Nibs/Xibs

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

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.

UIViewController default's behavior without XIB and without overriding loadView - documentation?

See the documentation for the loadView method:

If the view controller has an associated nib file, this method loads
the view from the nib file. A view controller has an associated nib
file if the nibName property returns a non-nil value, which occurs if
the view controller was instantiated from a storyboard, if you
explicitly assigned it a nib file using the initWithNibName:bundle:
method, or if iOS finds a nib file in the app bundle with a name based
on the view controller’s class name.

If the view controller does not
have an associated nib file, this method creates a plain UIView object
instead.

viewDidLoad not working (without NIB)

If you have no nib file you should be implementing the loadView method. See "Creating the View Programmatically" in the Custom View Controllers section of the View Controller Programming Guide. If you don't implement this method viewDidLoad will not be called.

Which should I use, -awakeFromNib or -viewDidLoad?

awakeFromNib is called when the controller itself is unarchived from a nib. viewDidLoad is called when the view is created/unarchived. This distinction is especially important when the controller's view is stored in a separate nib file.

Instantiate UIViewController programmatically without nib

According to the docs:

If the view controller does not have an associated nib file, this method creates a plain UIView object instead.

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/#//apple_ref/occ/instp/UIViewController/view

If you do not provide a nib file when instantiating the view controller, UIViewController calls it's loadView() method which creates a plain UIView object and assigns it to its view property.

The reason why you are seeing a transparent view is because the UIView object has no background color. To verify this, you can set the background color of the view controller's view property right before you push it on your navigation stack.

let viewController = TestViewController()
viewController.view.backgroundColor = .blueColor()
navigationController?.pushViewController(viewController, animated: true)

does loadView get called even if we don't override it in viewcontroller like the other ViewController lifecycle methods?

It always gets called. From the documentation,

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.

If the view controller has an associated nib file, this method loads
the view from the nib file. A view controller has an associated nib
file if the nibName property returns a non-nil value, which occurs if
the view controller was instantiated from a storyboard, if you
explicitly assigned it a nib file using the init(nibName:bundle:)
method, or if iOS finds a nib file in the app bundle with a name based
on the view controller'€™s class name. If the view controller does not
have an associated nib file, this method creates a plain UIView object
instead.

...

You can override this method in order to create your views manually.
If you choose to do so, assign the root view of your view 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.

It gets called even if you don't override it. Generally, you would only override it when you don't want to create a view controller from its nib. In this method, you would assign self.view some value (since view is loaded lazily). If you're not assigning some special subclass to your view property, you can usually get by by adding all your logic to viewDidLoad().

Here's a sample implementation:

// Say you have some custom view you want use as your controller's view
class CustomView: UIView { ... }

...
// In your view controller, you can set that custom instance to your view property.
override func loadView() {
self.view = CustomView()
}

UITableViewController, for example, sets a UITableView as your view (presumably) in this method.

By default, the view is just a vanilla UIView. If that's all you need, there's no reason to call this method at all. viewDidLoad() is still a perfectly good place to do any additional initialization.

A couple of things to remember:

  1. only assign to your view in loadView(). Don't invoke it (on the
    right-hand side; don't invoke its getter), because that can cause an
    infinite loop. If view is nil, loadView gets called to create
    it.
  2. Don't call super.loadView(). This method is meant to assign a view to your view property. By calling super, you'd be doing this twice.

A little more info on the infinite loop trap you could fall into:

From UIViewController's view:

If you access this property and its value is currently nil, the view
controller automatically calls the loadView() method and returns the
resulting view.

view is created and assigned in loadView() when it is nil. If you do that within loadView itself, you will prompt loadView to be called within its own body.

iPhone SDK: what is the difference between loadView and viewDidLoad?

I can guess what might be the problem here, because I've done it:

I've found that often when I add init code to loadView, I end up with an infinite stack trace

Don't read self.view in -loadView. Only set it, don't get it.

The self.view property accessor calls -loadView if the view isn't currently loaded. There's your infinite recursion.

The usual way to build the view programmatically in -loadView, as demonstrated in Apple's pre-Interface-Builder examples, is more like this:

UIView *view = [[UIView alloc] init...];
...
[view addSubview:whatever];
[view addSubview:whatever2];
...
self.view = view;
[view release];

And I don't blame you for not using IB. I've stuck with this method for all of Instapaper and find myself much more comfortable with it than dealing with IB's complexities, interface quirks, and unexpected behind-the-scenes behavior.



Related Topics



Leave a reply



Submit