viewDidDisappear not getting called on a UINavigationController - objective-c

I'm having a problem with my navigation controller. If I add a view controller to the stack:
- (void) tui_ToggleListStudy:(id)sender
{
listVC = [[ListViewController alloc] init];
[self.navigationController pushViewController:listVC animated:NO];
[listVC release];
}
I have NSLog messages for the view controller beneath, for both viewWillDisappear: and viewDidDisappear - but only viewWillDisappear: is getting called.
Not only that, but the view controller doesn't receive any other delegate messages either: No viewDidUnload, or dealloc...
Is there anything I can do about this?
I'm stumped! Any thoughts?
Thanks!

I know the answer if you made the same typo in your code that you made in your question: the method signature is viewDidDisappear: (with the animated argument), not viewDidDisappear.
Not only that, but the view controller doesn't receive any other delegate messages either: No viewDidUnload, or dealloc...
A view controller will not be deallocated when you push another controller onto the stack. And viewDidUnload won't be called unless you run out of memory.

Assuming your navigation controller is contained in some kind of top view controller, you must also forward the relevant messages from that top view controller to the nav controller:
-(void)viewWillAppear:(BOOL)animated
{
[navController viewWillAppear:animated];
}
-(void)viewDidAppear:(BOOL)animated
{
[navController viewDidAppear:animated];
}
-(void)viewWillDisappear:(BOOL)animated
{
[navController viewWillDisappear:animated];
}
-(void)viewDidDisappear:(BOOL)animated
{
[navController viewDidDisappear:animated];
}

You must call super at implementation of viewWillDisappear.

The designated initializer for UIViewController is -initWithNibName:bundle:. Are you sure your view controller is finding its nib and finds its connected view? I'll bet if you set a breakpoint after init'ing your ListViewController, you'll find its -view returns nil.

Related

How do I catch when UIViewController displays after dismissViewControllerAnimated

I've got two UIViewControllers. I'm using modal segue to the second one, when coming back I use dismissViewControllerAnimated. I want to fire a method when I come back to the first one. How can I do that?
I tried to fire a custom notification before dismissViewControllerAnimated and catching it in the first UIViewController, but it doesn't catch anything, because it's still on the second one when it's fired.
There are easy options I can see.
Use the viewDidDisappear: method in the view you're dismissing.
dismissViewControllerAnimated:completion: method accepts a block that actually executes after viewDidDisappear executes in the dismissing view.
To pass a reference from one view controller to the next:
In the second view controller's .h file, add a property:
#property (nonatomic,strong) FirstViewController *firstVC;
In your first view controller, add the following method:
- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender {
if([#"segue_YOUR_SEGUE_NAME" isEqualToString:[segue identifier]]) {
if([[segue destinationViewController] isKindOfClass:
[SecondViewController class]]) {
SecondViewController *dest = (SecondViewController*)[segue
destinationViewController];
dest.firstVC = self;
}
}
}
Now, in your second view controller, you can do two things, as I already stated:
[self dismissViewControllerAnimated:YES
completion:^{
[self.firstVC someMethod];
}];
OR...
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self.firstVC someMethod];
}

Attempt to present * on * whose view is not in the window hierarchy

I'm trying to make a modal view controller in my app delegate (I created a function called showLoginView). But whenever I try to call it I get a warning in XCode:
Warning: Attempt to present <PSLoginViewController: 0x1fda2b40> on <PSViewController: 0x1fda0720> whose view is not in the window hierarchy!
Here's the method code:
- (void)showLoginView
{
PSLoginViewController *loginViewController = [[UIStoryboard storyboardWithName:#"MainStoryboard" bundle:NULL] instantiateViewControllerWithIdentifier:#"PSLoginViewController"];
[self.window.rootViewController presentViewController:loginViewController animated:NO completion:nil];
}
How can I add the view to the window hierarchy? Or maybe I'm doing something very wrong?
You can't display a modal view controller from the appDelegate. You need to display a modal ViewController from whichever viewController is currently displaying full-screen. In other words, you need to put that code into your root view controller, or whichever one you want to display the modal vc from...
Also, you'll want to use the method "presentModalViewController" to present the modal. You can set properties on the modal vc such as:
vC.modalPresentationStyle = UIModalPresentationFormSheet;
vC.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:vC animated:YES];
You can actually present a modal view Controller from the AppDelegate as long as you detect the current visible viewController and take care of the case where you current controller is a navigationController.
Here is what I do:
UIViewController *activeController = [UIApplication sharedApplication].keyWindow.rootViewController;
if ([activeController isKindOfClass:[UINavigationController class]]) {
activeController = [(UINavigationController*) activeController visibleViewController];
}
[activeController presentModalViewController:loginViewController animated:YES];
UIViewController *activeController = [UIApplication sharedApplication].keyWindow.rootViewController;
if ([activeController isKindOfClass:[UINavigationController class]])
{
activeController = [(UINavigationController*) activeController visibleViewController];
}
else if (activeController.modalViewController)
{
activeController = activeController.modalViewController;
}
[activeController presentModalViewController:vc animated:YES];
I ran into this problem on iOS 7 - the key to making any of the proposed solutions work was to call
[self.window makeKeyAndVisible];
in your AppDelegate.
After that call, presenting a modal view from the window's rootViewController worked.
Another reason for that warning can be that you want to present a view controller from an instance which is not the top most view controller.
So first you have to get the topmost UIViewController and using this instance to call presentViewController:
UIViewController *root = [UIApplication sharedApplication].keyWindow.rootViewController;
while (root.presentedViewController) {
root = root.presentedViewController;
}
You can NSLog(#"%#", self.window.rootViewController), and see what the rootViewController really is.
I came into this problem, when the rootViewController is a normal UIViewController.
Replace it with a UINavigationController, wish it will help.
Faced this issue while trying to present controller from the call of delegate of other controller . i.e : show search filter with delegate , once done back to my controller and receive data via the delegate then present controller , all I had to do is to dispatch the present code cause while in a delegate you're in another thread , that's why you're presenting on your view from main thread another controller from that other thread , so have to go back to main thread , just put the presenting code like this :
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:searchVC animated:true completion:nil];
});
Hope this helps !

Not calling [super dealloc] inside a static UIViewController subclass

In my app I have a UIViewController subclass (VC for short) that I only use in one place in the entire app. In that place, I have been creating and pushing it like this:
MyViewController* VC = [MyViewController new];
[self.navigationController pushViewController:VC animated:YES];
[VC release];
but I was thinking that since this is the only place I am using a view controller of this type, I could do something like this so the settings used won't be reset each time the view controller is pushed onto the stack:
static MapsToSendPicker* VC = nil;
if(!VC) {
VC = [MapsToSendPicker new];
}
[self.navigationController pushViewController:VC animated:YES];
[VC release];
The problem with that code is that in VC's dealloc method, I release all of my instance variables and set them to nil, and finally I call [super dealloc]. This deallocates the static view controller, but the test if(!VC) isn't evaluated to true after (this would defeat the purpose of the whole idea if it were; then I'd have to recreate the view controller each time anyway).
My solution is overriding the dealloc method in MyViewController and not calling [super dealloc] at the end. This works, but the compiler raises a warning. How can I get rid of that warning while maintaining the functionality I gain with this design? Thanks!
Edit:
After a quick Google search, I have found this solution:
- (void)dealloc {
if(NO) {
[super dealloc];
}
}
but I would like something a little bit... cleaner. Any thoughts?
Remove the [VC release]; line and add [super dealloc] back. Everything will work properly and dealloc will never get called. Generally you should consider using NSUserDefaults in order to restore the VC properties instead of keeping the controller in memory all the time.

Identify when I return to ViewController

I have two views with their controllers.
The application start with the FirstViewController.
Then with a button I open the SecondViewController.
With other button I dismiss the SecondViewController to return to FirstViewController.
Is there any way to detect that in FirstViewController that it has recovered focus?
EDIT: Ok I look the answers and I use viewWillAppear but don't work if I use a UIModalPresentationFormSheet.
-(IBAction)openSecondView{
SecondViewController *screen = [[SecondViewController alloc] initWithNibName:nil bundle:nil];
screen.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
screen.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:screen animated:YES];
[screen release];
}
And close this view with close button.
viewWillAppear never called.
implement UIViewController's viewWillAppear method
- (void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
}
Add a delegate (Protocol) method. Call the delegate method just before dismissing the SecondViewController.
For more info on delegate, see delegate
implementation of the viewWillAppear: callback will work only if you use navigation controller or tabbarcontroller for displaying another controller + with such approach you will need to check somehow if this is only the first appearance of the view, or was called by any other reason;
using a delegate, as described by Gomathi above is a much better option!
Depends how you are setting up the first view controller. Encapsulate it within UINavigationViewController (and if you do not want the navigation bar, you can always set it to hidden ( [self.navigationController.navigationBar setHidden:YES] ). ViewWillAppear will work then.
inside the viewWillAppear of the FirstViewController
- (void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
NSLog(#"view 1 focused");
}

How to switch to another view

The following code should work, right?
ViewController2 *childView = [[ViewController2 alloc] initWithNibName:#"ViewController2" bundle:nil];
[self.navigationController pushViewController:childView animated:YES];
[childView release];
It doesn't do anything though. There are no error messages. The view just doesn't get switched. viewDidLoad does not even execute inside ViewController2.
That code won't do anything if the view controller presenting it doesn't have a navigation controller, i.e. it isn't in a navigation controller stack. In that case, you'll be calling a method (pushViewController:animated:) on a nil object (self.navigationController) which does nothing. Thus, you can only use this this method if the "parent" view controller is in a UINavigationController stack.
Use this:
[self presentModalViewController:viewControllerNameHere animated:YES];