Division of responsibilities for loadView() and viewDidLoad - ios

In a project I'm working on, I have a view controller which overrides the loadView() method for setting up the view hierarchy programatically. I was curious if there are requirements regarding what goes in this method versus in viewDidLoad(), or if the latter is redundant when loadView() is already overridden. Is there anything that cannot be done e.g. at the very end of loadView() that should be deferred until viewDidLoad() is called, or is it simply a matter of preference?
EDIT: The initial phrasing of this question was somewhat opinion based. To clarify, I'm interested in whether there is any reference material indicating that there are technical limitations regarding methods that are not able to be used in loadView() that can be used in viewDidLoad().

The documentation for loadView pretty much answers your question:
If you use Interface Builder to create your views and initialize the view controller, you must not override this method.
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.
If you want to perform any additional initialization of your views, do so in the viewDidLoad() method.
In other words, only ever override loadView if you are writing your view controller strictly in code - no nib or storyboard. And then only if you have a reason for the base view of the controller to be something other than a plain old UIView that you get by default. And remember that you must not call super.loadView() in your implementation.
I would use viewDidLoad to create and setup any subviews you wish to add to the base view of the view controller (whether loadView was used or not). Also use viewDidLoad for any other setup that can't be done until the view has been loaded.
Technically, if you override loadView to setup a custom base view, anything you might have put in viewDidLoad can be put in your loadView. But I would still separate the responsibility. Only use loadView (if needed) to create and setup the base view of the controller. Use viewDidLoad to create other subviews as needed and any other controller setup.
Also keep in mind that some view controller initial setup belongs in the initializer. But such code can't make any reference to any views.

Related

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

Does loadView get called even if we don't override it in view controller like the other ViewController lifecycle methods?
Even if we don't override the ViewDidLoad method, we know this lifecycle method will get called internally by ios. Does the same thing happen for LoadView also? or does it gets called only when the view inside the VC is nil or when we explicitly override it?
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:
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.
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.
Yes, it will still get called. You'll observe the same behavior as if you were to add this to your view controller.
override func loadView() {
// Only call this to observe this is being called.
// If you want to write your own implementation, you shouldn't call super.loadView().
// Instead, create your own UIView instance and set self.view = yourView.
super.loadView()
}

How to use a UIView delegate in initialization stage

I have a view controller which has a couple of views. Those views need data from the model and I use the view controller as a delegate to supply them that data.
The problem is that they need some of that data when they are initialized (in initWithFrame/awakeFromNib). At that stage the delegate is not set yet (it is set in the view controller's viewDidLoad which is called after the view is initialized).
I can solve it by just accessing the model directly from the view, but that will create quite a mess if every view would access models directly.
Where can I set the delegate in order to use it in the view's awakeFromNib/init?

Un-override loadView for storyboard project

My iOS 5 project uses storyboards. I have a view whose view controller inherits from a class that in turn inherits from UIViewController. This class belongs to a framework that was written for non-storyboard projects, i.e. it initialises the view by overriding loadView. When my view controller runs, it receives self.view as set up by the super class, thus rendering ineffective the Interface Builder settings for the current view.
While I do have the source code of the framework I'm using, and in theory could edit it to remove the loadView override and relocate the code to viewDidLoad for example, I'm wondering if there is a better way to do this just from my own code. Basically I would need to have my [super loadView] un-overriden, or bypass it by calling my superclass's superclass's (i.e. UIViewController) loadView. Is there any way to achieve this?

Is it ok to call [super loadView]?

I thought that I should never call [super loadView] but something is confusing me.
In description of loadView (UIViewController Class Reference) it is said that "Your custom implementation of this method should not call super.",
but in ZoomingPDFViewer example that they gave, loadView implementation (ZoomingPDFViewerViewController) is calling [super loadView].
I have tried to call it from my loadView method and it works ok, but I just don't understand then what does it mean to not call super.
You definitely should not be calling [super loadView]. I'd say you found a bug in the ZoomingPDFViewer example.
You override loadView when you want to programatically create the view hierarchy for your view controller (not using a xib).
As you pointed out, the docs clearly state that you should not call super.
Your custom implementation of this method should not call super.
I assume this is to avoid loading both from a xib and programatically creating a view as this method is used by the base to load a view from a xib:
If the view controller has an associated nib file, this method loads
the view from the nib file.
Note also that even if during allocation of your UIViewController object you pass nil for the nibNameOrNil parameter that the UIViewController implementation of loadView will try to load any xib with the associated class name in it.
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.
The real intent of this method is to give you full control of building the view hierarchy without relying on the built in xib loading mechanism.:
You can override this method in order to create your views manually.
Personally, I override loadView if: 1.) The xib I would make for it is really trivial or 2.) The layout of the control is very dynamic, so creating a xib with a static layout has little benefit.
NSViewController tries to initialize a view from a nib in -loadView. Since your nib name is not set for your controller, it will just give you a self.view = nil; I would assume UIViewController works the same way.
So it should be safe, but you it's completely unnecessary.
If you dont have a view created in your IB, then you should call [super loadView] in your code to give a view to your program.
In case of your custom views, you are suppose to create a view with the interfaz builder, so you dont need to call it.
If you create your ViewController programmatically, you could call super.loadView() instead of self.view = UIView(frame: UIScreen.main.bounds) at the beginning of override func loadView().
However, do NOT call self.view before super.loadView(), since the former will trigger the latter if view did not been loaded.
I don't think it's a good idea to explain Apple's documentation like a robot.

Should [super loadView] be called from loadView or not?

In Programming iOS 4 by Matt Newburg he states:
“To provide a UIViewController with a view manually, implement its
loadView method… You must NOT call super”.
In the iOS 5 Developer's Cookbook by Erica Sadun she states:
“The loadView method allows you to set up the screen and layout any
subviews… Make sure to call [super loadView] whenever you inherit
from a specialized subclass such as UITableViewController or
UITabBarController.”
This, to me at least, is confusing.
Apple is the source of truth and they say NO super call.
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.
https://developer.apple.com/documentation/uikit/uiviewcontroller/1621454-loadview
[edit]
Another important note scattered around in the UIViewController class reference:
The default loadView method attempts to load the view from the nib
file associated with the view controller (if any).
This is a very old question, but I find that it needs a better answer than the one it got.
Should [super loadView] be called from loadView or not?
It depends. The two sources you cite are talking about different situations, and they're both correct in the context they're describing.
The quote from Neuberg is talking about view controllers that inherit directly from UIViewController. That class has its own implementation of loadView that provides default behavior; specifically, it automatically loads the view hierarchy from a .xib (or storyboard) file that's associated with the view controller. If you call UIViewController's version of that method, the view hierarchy created in that method will either replace your own implementation's view hierarchy, or vice versa. Nine years after this question was posed, the documentation for UIViewController's -loadView method still warns against that:
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. [emphasis added]
The quote from Sadun is talking about a different situation, i.e. one in which your view controller is not a direct subclass of UIViewController, but is instead derived from UITableViewController, UITabBarController, etc. Those classes override -loadView themselves and need their versions called. At least in the case of UITableViewController, this is called out in the Overview section:
You may override loadView or any other superclass method, but if you do, be sure to invoke the superclass implementation of the method, usually as the first method call.
So, if you're subclassing UIViewController and providing your own -loadView implementation to generate the controller's views rather than using a .xib or storyboard to provide the views, don't call the superclass's -loadView method. On the other hand, if you're subclassing a class such as UITableView and doing the same thing, check the docs to see whether you need to call that class's -loadView method from your own override.

Resources