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
andUITabBarController
.) - If a view is added “directly” to a
UIWindow
, itsUIViewController
(if any) will receiveviewWillAppear
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.