31

In trying to figure out why viewWillAppear wasn't being called in my app I came across what may be a gross misunderstanding I hold about the intended use of UIViewController subclasses.

According to the following post viewWillAppear does not run when using addSubView! and the link to this blog post: http://blog.carbonfive.com/2011/03/09/abusing-uiviewcontrollers/ UIViewController subclassing should only happen in very specific situations. Most notably when added directly to the UIWindow, or other Apple created custom controllers like UINavigationControllers.

I'm certainly guilty of adding the views of UIViewController subclasses to the views of other UIViewController subclasses.

In fact, I thought this was more or less the whole idea of Apple's implementation of MVC generally... One overall VC, with other VCs beneath, all happily getting their delegate methods called.

If there are a lot of views (which by definition need controlling) coming and going in an app, and lots of screenfulls, in the model described in that post, each screenfull should have one master VC Subclass, with all the subviews controlled instead by custom controllers (which happen to control views) which are subclasses of simple NSObject.

In this case, UIViewControllers should only be directly to the Window or UINavigationController, UITabBarController etc?

Are you guaranteed to get the UIVC Delegate methods called in that case? How Does this differ from calling the delegate methods manually when a viewcontroller's view is a subview of another VC?

Honestly this seems like a tremendous waste of time. Custom implementations of ViewDidLoad, viewDidLoad, viewDidUnload, viewWillAppear, viewWillDisappear not to mention things as simple as properties like, say, "view"...

So basically, either I have been completely wrong, or I'm on a wild goose chase. If UIViewController subclasses can't be counted on to call viewWillAppear, why not just call that method manually, and be done with it?

Why replicate all of the perceived functionality of UIViewController?

Community
  • 1
  • 1
averydev
  • 5,587
  • 2
  • 32
  • 35
  • 1
    yup these days (2014) you trivially do this with containers http://stackoverflow.com/a/23403979/294884 - cheers – Fattie Aug 31 '14 at 16:08
  • As I remember, thats the way it's done for Cocoa (not Cocoa Touch). Separate NSViewController subclasses for different parts of the UI. But very nice question for Cocoa Touch which helped me to clearly realize this difference! – jan.vogt May 30 '16 at 19:18

1 Answers1

38

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.

Tyler A.
  • 2,914
  • 1
  • 20
  • 26
Caleb
  • 120,112
  • 19
  • 171
  • 259
  • 4
    Thank you for the thorough and exceptionally clear explanation. Having been confused on that point early on, I never went back to inspect my assumptions regarding this, and read View Controller Programming guide with that mistaken idea in place. Thinking on it now, some of the issues that I've been baffled by in the past are clearly the outcome of this misunderstanding, particularly the behavior of viewDidUnload and viewWillAppear. Not to mention, I can only presume that the memory and performance my apps will both improve going forward... – averydev Apr 17 '11 at 08:34
  • 2
    ~2 hrs later code refactored to properly implement the above pattern, and it works beautifully. Won't make that mistake again! – averydev Apr 17 '11 at 19:17
  • @Celeb, @AVeryDev: you suggest to create a "generic" view controller by subclassing NSObject, so this subclass will have -viewDid[Load|Unload|Appear|Disappear] and -viewWill[Appear|Disappear] - will these methods be called automatically for this subclass and its "subcontroller" classes? If no, is there a way to ensure this? – matm Jun 03 '11 at 09:21
  • @delirus, no, they won't be called automatically. You'll have to call them yourself, perhaps from your real view controller's versions of the same methods. – Caleb Jun 03 '11 at 14:52
  • 1
    @caleb - are you sure this is correct? The UIViewController Class Reference says "Each view controller typically represents a distinct set of behavior. For example, one view controller might manage a table of items while a different one would manage the display of a selected item from that table." - I am trying to modify my code to incorporate your suggestions but hit a wall as I use xib files for my existing "sub" ViewControllers - Any suggestions? – amergin Nov 12 '11 at 16:27
  • 3
    @amergin Things changed somewhat with iOS 5 in that you can now create your own container view controllers (see [Implementing a Container View Controller](http://goo.gl/KZ7Po)), so now in some cases you may have more than one view controller active at the same time, but you definitely won't need anything like one controller per UIView instance. Beyond that, the above is correct as far as I know. If you're having trouble you might want to post your own question -- it's hard to give good help in comments. – Caleb Nov 12 '11 at 19:17
  • 1
    @Caleb - sorry, I read more and worked on the thing and now understand it far better. – amergin Nov 15 '11 at 00:16