Here is the pitch.
The above solution not works for me. My case is the custom view controller nested under a complex UINavigationController not gets called viewWillAppear and viewDidAppear. Use below in custom view controller:
beginAppearanceTransition(true, animated: animated) // Tells a child controller its appearance is about to change. Do not invoke viewWillAppear(_:), viewWillDisappear(_:), viewDidAppear(_:), or viewDidDisappear(_:) directly.
endAppearanceTransition() // Tells a child controller its appearance has changed.
My problem was similar to this only.
CustomTabBarController -> CustomUINavigationController -> RootViewcontroller
viewWillAppear of CustomUINavigationController and RootViewController are not getting called unless you switched to another tab and come back.
The solution is call super.viewWillAppear(animated: true)
override func viewWillAppear(_ animated: Bool) {
**super.viewWillAppear(true)**
}
I struggled for more than a day for this small mistake.
NOTE: this was written in 2013. Changes to the way iOS handles view hierarchies nowadays may render this solution useless and/or dangerous. So use at your own risk.
Original Answer When nesting a custom UIViewController under a UINavigationController the methods viewWillAppear and viewDidAppear of the custom viewController may not be called depending on the complexity of your view controller hierarchy (think modal views, navigation controller inside tab view controller...). So if you find yourself in this situation what can you do to ensure these two methods are called?
The answer...
This is a very elegant method to implement for it does not rely on any assumptions regarding when the controller will be loaded by the navigation controller.
There are two methods available:
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
Here is how the code would change.
You need to declare that your CustomViewController implements the UINavigationControllerDelegate protocol:
@interface CustomViewController : UIViewController <UINavigationControllerDelegate>
You need to set your CustomViewController as the delegate of the UINavigationController where you initialize it.
Last you must also add your custom implementation of the UINavigationControllerDelegate methods to your CustomViewController class implementation. For instance you can implement the navigationController:willShowViewController:animated:
method so that:
List item
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if ([viewController isEqual:self]) {
[viewController viewWillAppear:animated];
} else if ([viewController conformsToProtocol:@protocol(UINavigationControllerDelegate)]){
// Set the navigation controller delegate to the passed-in view controller and call the UINavigationViewControllerDelegate method on the new delegate.
[navigationController setDelegate:(id<UINavigationControllerDelegate>)viewController];
[[navigationController delegate] navigationController:navigationController willShowViewController:viewController animated:YES];
}
}
And the navigationController:didShowViewController:animated:
can be implemented simply as follows:
- (void)navigationController:(UINavigationController *)navigationController
didShowViewController:(UIViewController *)viewController
animated:(BOOL)animated
{
if ([viewController isEqual:self]) {
[self viewDidAppear:animated];
}
}
The benefit of this approach is really that you solely rely on the way the UINavigationViewController is supposed to work and you make your calls just at the right time. It also allows you to pass the delegation around as you move up and down the navigation controller hierarchy right before the viewWillAppear method is called.
Again for simple hierarchy this may not be required. But if you ever find yourself in a situation where your viewWillAppear
and viewDidAppear
methods are not called you now know what to do...
it should be done as follows:
See (*1) edit
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
CustomViewController *controller = [[CustomViewController alloc] initWithNibName:nil bundle:nil];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller];
[controller release];
self.window.rootViewController = navController; //(*1)
[self.window makeKeyAndVisible];
[navController release];
return YES;
}
One reason this will happen is if you override viewDidAppear:
in your UINavigationController
subclass and don't call [super viewDidAppear:animated];
...
It is 2015 now and you probably don't need to use the UINavigationControllerDelegate methods as in the accepted answer. Just check carefully your code if you have any typo or copy/paste error.
I ran into an issue lately that viewDidAppear
is no longer called after some copy/paste. After reading @Yar's answer, I did a search on viewDidAppear
in my code and found that [super viewDidAppear:animated];
was mistakenly called in viewWillAppear
:
-(void)viewWillAppear:(BOOL)animated
{
[super viewDidAppear:animated];
//... ^^^
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// this is never called :(
}
Just share this finding here in case people run into same issue.