viewWillAppear

Some posts are of general interest, some are more narrow in their focus. This is a small post, on a specific question, to which I don’t even have all the answers. But if you’re working with Cocoa on the iPhone, and having problems with viewWillAppear (or anything else in the view*[Aa]ppear family), have I got a post for you.

Motivation

The viewWillAppear message is sent to instances of the UIViewController class (or one of its subclasses) “before the view [managed by the controller] appears and any animations begin”. (I find it a useful place to reload table views.) Unfortunately, in a common use case (a UITableViewController pushed onto a UINavigationController) the viewWillAppear handler of the table view controller does not execute.

Resources

This problem has been kicked around on the web a good deal, it’s discussed

Generalization

The underlying problem appears to be that viewWillAppear messages are sent only to the “topmost” UIViewController; they do not automatically propagate down a view hierarchy. Apple’s docs somewhat cryptically address this:

Warning: If the view belonging to a view controller is added to a view hierarchy directly, the view controller will not receive this message. If you insert or add a view to the view hierarchy, and it has a view controller, you should send the associated view controller this message directly. Failing to send the view controller this message will prevent any associated animation from being displayed.

I say this warning is cryptic for two reasons:

  • What does it mean to add a view to a view hierarchy “directly”? Aren’t almost all views added to hierarchies “directly”? (The only exceptions I can think of involve the subviews of UINavigationController and UITabBarController.)
  • If a view is added “directly” to a UIWindow, its UIViewController (if any) will receive viewWillAppear messages. (I have no idea how.)

The upshot of this is that, while a UINavigationController will, in fact, forward viewWillAppear messages to its children (it just seems to be coded that way) a UINavigationController won’t necessarily receive viewWillAppear messages in the first place.

Example

Let’s say you’ve created a new Application in Xcode, based on the “Utility Application” template. Furthermore, let’s say you’ve added a UINavigationController to the “main” view, with the following code:

@interface MainViewController : UIViewController {
    UINavigationController* nc;
}

@end
@implementation MainViewController

// ... snip ...

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        // RoundViewController is defined elsewhere
	RoundViewController* root = [[RoundViewController alloc] initWithStyle:UITableViewStyleGrouped];
	nc = [[UINavigationController alloc] initWithRootViewController:root];
	[root release];
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:nc.view];
}

// ... snip ...

@end

Then, you’ll need to add the following code to the @implementation of the MainViewController class:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [nc viewWillAppear:animated];
}

and this code to the @implementation of the RootViewController class:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    if ([mainViewController.view superview] != nil)
    {
        [mainViewController viewWillAppear:animated];
    }
}

Sort of ugly, but that seems to be how it is.

Share and Enjoy:
  • Twitter
  • Facebook
  • Digg
  • Reddit
  • HackerNews
  • del.icio.us
  • Google Bookmarks
  • Slashdot
This entry was posted in iPhone. Bookmark the permalink.

Comments are closed.