Am I abusing UIViewController Subclassing?

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.

Leave a Comment