The view loading system on the iPhone works like this:
When you initialize a view controller (either with -init or -initWithNibName:bundle:), it doesn't actually create and initialize the view. When you call -view for the first time, it calls -loadView. By default, -loadView just loads the view from the xib file (nibName). If you override this, though, you're responsible for creating the view and assigning it to the view controller's view property. As an example:
- (void)loadView
{
UIView *view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
// add subviews
self.view = view;
[view release];
}
Every time you create the view, which is different from the view becoming visible and showing onscreen, it calls -viewDidLoad. (-viewDidAppear/-viewDidDisappear is for the visibility of the view on-screen)
Since we're already off-track, let's consider memory management. When the view is offscreen, the system will automatically set the view property of a view controller to nil. The problem is that all the subviews of that view are leaking. How so? Well, the retain count for each subview is 2 (views retain subviews, and your view controller has an outlet/ivar to it). When the view is nil, the retain count of that view is 1. It doesn't make sense for a view to stick around if a view isn't showing, so you set it to nil in -viewDidUnload (which is a hook for whenever the view is set to nil).
Answer to title question: Yes.
So basically, either I have been
completely wrong, or I'm on a wild
goose chase.
It sounds like you've been completely wrong. The term "view" has a few different but related meanings:
- A view is, of course, any object that's an instance of UIView or a subclass of UIView.
- In the context of MVC, "view" is used collectively, and we talk about this or that being "the view's responsibility" even though "the view" is really a group of objects.
- When talking about a view controller, the "view" that the controller manages is the UIView instance that the controller's view points to and the hierarchy of subviews that it contains.
It sounds like your misunderstanding is on this last point. A view controller should manage a single "screenful" of content. If you're using a single view controller object to manage more than one view hierarchy, or if you're using several view controllers to manage different parts of the same view hierarchy, you're using UIViewController in a way which was never intended and which is likely to lead to problems.
The methods that you mentioned (-viewDidLoad, -viewWillAppear, etc.) are meant to tell the view controller that its view hierarchy was just loaded, is about to be displayed, and so on. They're really not meant to refer to an individual subview, and it would be unusual for a view controller to need to be given that information for individual subviews. If the view hierarchy was loaded, then the view controller knows that everything in that hierarchy was loaded.
You seem to be interpreting these methods as delegate methods, but they're not. A delegate is a separate object that allows for customization of the delegator without the need for subclassing. -viewDidLoad
and -viewWillAppear
are two examples of override points for UIViewController, a class that's intended for subclassing. The view controller object calls these methods itself to give subclasses a chance to take some action at an interesting point in the controller's life cycle.
If UIViewController subclasses can't
be counted on to call viewWillAppear,
why not just call that method
manually, and be done with it?
Take a good look at UIViewController and you'll see that most of the functionality provided has to do with displaying the view (that is, the view hierarchy) on the screen, or with integrating the controller with "container" view controllers such as UINavigationController and UITabBarController. None of that is useful to objects that aren't managing the entire screenful of content.
It happens sometimes that a group of views will replicated on several screens, and in some of those cases it's helpful to manage those views with an object that's separate from the view controller itself. I can see how you'd be tempted to use UIViewController because of its -viewDidLoad and similar methods, but those are really only a small part of what UIViewController does. What would it mean to call -presentModalViewController:
on one of those objects? Or to access its navigationController
or parentViewController
properties?
If you really want to manage subviews of your view controller's view hierarchy using those methods, create a subclass of NSObject that has -viewDid[Load|Unload|Appear|Disappear] and -viewWill[Appear|Disappear] methods. You can create that class once and then subclass it as often as you need to, and none of your "subcontroller" classes will have all the extra, unneeded controller management stuff that comes along with UIViewController.
Edit: I want to add a pointer here to Apple's View Controller Programming Guide for iOS, which provides a lot of support for what I've laid out above. Here's a relevant passage from the subsection titled "View Controllers Manage a View Hierarchy":
View controllers are directly
associated with a single view object
but that object is often just the root
view of a much larger view hierarchy
that is also managed by the view
controller. The view controller acts
as the central coordinating agent for
the view hierarchy, handling exchanges
between its views and any relevant
controller or data objects. A single
view controller typically manages the
views associated with a single
screen’s worth of content, although in
iPad applications this may not always
be the case.
View Controller Programming Guide is required reading for anyone even thinking of writing an iOS app. It's worth reviewing if you haven't read it in a while (or ever).
Update: Starting with iOS 5, it's now possible to define your own container view controllers, i.e. view controllers that manage other view controllers and potentially display the views of multiple view controllers at the same time. You can read more about it in the guide linked above in the section entitled Creating Custom Container View Controllers. None of this really changes the essential points above: a single view controller should still manage a hierarchy of views, and methods like -viewDidLoad
still refer to the entire view graph rather than to individual subviews. The advice that a view controller manages an "entire screenful" of content is no longer completely accurate -- just as UISplitViewController
has displayed content from two view controllers simultaneously ever since the introduction of the iPad, your own containers can now show the views of multiple child view controllers. Writing a container view controller is a somewhat advanced topic -- you should be very familiar with the use of view controllers generally and the way the provided container view controllers work before you take a stab at creating your own.
Best Answer
A
UIViewController
will create its view (by loading it from a nib or implementing-loadView
) when the controller'sview
getter is called and its view is currentlynil
.In the code shown you never invoke the view property's getter, only its setter.
Also, you are assigning the controller's view from your app delegate.
UIViewController
s are expected to create their own views on demand, not have them provided by some other class. This approach will cause you problems later when you realize that the controller unloads its view and attempts to recreate it in response to memory warnings. Let your controller create its view, don't try to pass it one.