viewDidDisappear not called - ios

I work on an application, where I have a problem with my view[Will/Did]Disappear methods not being fired when returning to the app.
The case is, I have UINavigationController, which has two view controllers pushed on it. When the user presses the home button, the user is logged out. When he later returns to the app, the following (simplified) code is run in my AppDelegate:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[(UINavigationController *)self.window.rootViewController popToRootViewControllerAnimated:NO];
[self.window.rootViewController presentModalViewController:loginViewController animated:NO];
}
When I pop off the view controllers on my navigation controller stack, I would expect the view[will|did]disappear methods to be called. However, this is not the case, since they are (apparently) not on the screen anymore when iOS are going to fire these methods. It seems that the modal view controller has taken over.
If I do not present the modal view controller, the view[will|did]disappear methods are called as expected.
My question is: If I want the view[will|did]disappear methods to be called, how can I then structure my code? Is there a better place to present my modal loginViewController?
Edit:
In order to show my problems more clearly, I have created a very simple test project here: https://github.com/JohanVase/ModalViewCauseMissingViewDisappearCalls. Please try a couple of times to follow the instructions in the app, and see that I do not get my "resources" released in my viewWillDisappear method.

I finally asked Apple Technical support the same question. They concluded that this was a bug in iOS, so I have filed a bug report to Apple. The same bug seems to appear in iOS 6 and in the latest iOS 7 (Beta 5).
Apple Technical Support suggested the following:
As a workaround, you can move your cleanup code to a separate method
which the AppDelegate would then invoke on the navigation controller's
top view controller, before it pops the entire navigation stack.
However, I think this exposes too much of my details in the view controller, so I chose to implement it using willMoveToParentViewController: instead. This method is called when the view controller is removed from its parent, and it is called properly.

Related

self.presentingViewController broken on iOS 8?

I'm experiencing something really weird :
Create an extremely basic single view project, and add a second view controller to the storyboard, along with a modal segue from the first to the second. Initiate the segue from the view controller and trigger it programmatically with performSegueWithIdentifier:.
In the viewDidLoad of the modally presented view controller, add this log :
NSLog(#"%#", self.presentingViewController);
Now run the app on iOS 7, you should get a log like this one :
<ViewController: 0x7fa8e9530080>
Which is just the reference of the initial view controller of the app, which presented the modal view controller.
Now run the exact same thing on iOS 8, and you will get :
(null)
What's going on here ? Is it a known issue ? Of course I'd expect the exact same behavior on both systems.
Thanks ... Formalizsed as answer.
viewDidLoad should really be used for initialization, At this stage, there is not guarantee that the receiver's controllers view hierarchy has been placed in the navigation tree. If that is your intent, you should override viewWillAppear or viewDidAppear. Whilst it works in earlier versions, the docs clearly state that it should be used for additional initialization. It certainly sounds as though in iOS 8, the receiver's initialization is being performed earlier

iOS 8 viewDidLoad modal presentation causes multiple presentations

So I've got a screen that does a check for certain attributes and under defined circumstances will instantly load another view modally in viewDidLoad, without animation, over the currently-loading view (so as not to show the view below). Prior to iOS 8 when this was done, the original view would pause its loading (would not proceed with viewWillAppear, viewDidLayoutSubviews etc.) until the overlaying controller was dismissed. This behaviour I found was appropriate for my needs, as any animation on elements in the original view, could then be done. However, in iOS 8 I'm getting a completely different chain of events. First off, for some reason viewDidLayoutSubviews is being called twice (what's up with that?) but more importantly the view is not liking another controller being popped up at all anytime before viewDidAppear, complaining about unbalanced calls to begin/end appearance transitions. Not only that, but the underlying viewController continues with it's loading (viewWillAppear,viewDidLayoutSubviews etc.) even though it's not being shown which causes all the methods in those events to fire. I appreciate if Apple have updated the way something like this is meant to be achieved, so if the new meta is a completely different process I'm willing to adopt, however, as it is I can't get this to work appropriately.
I'd appreciate any help on how to get this modal view to interject without causing the underlying view to continue it's loading.
Thanks,
Mike
UPDATE: Going to bring some code in. Below is the viewDidLoad of the main viewController that presents the modal VC if need.
-(void) viewDidLoad{
if(hasNotSeenTutorial){
TutVC* vc = [[TutVC alloc] initWithNibName:#"tutNib" bundle:nil]
vc.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self.navigationController presentViewController:vc animated:NO completion:^{
NSLog(#"Has Completed Presentation");
}];
}
}
This is where the issues are. Calling the presentation here in viewDidLoad, causes the presentation of the presenting VC to continue. Prior to iOS 8 the presenting VC if not yet presented, would pause, until the modal VC had been dismissed, it would then complete as usual. This is not the case in iOS 8, as per my original post.
Apple has made its rules stricter with ios 8. To give you an example and I ll drive my point through this:- In my app i used to pop some view controllers off the navigation stack and just after that, push the a new one, but that pop was never seen in ios7, only a push transition appeared to happen (when logically, pop should have been seen and then the push). And in ios 8 this thing changed. Now a push is seen only after the pop is seen and noticed. which breaks the UX rather badly.
I have noticed this strictness in other areas as well but those are not UI/UX related so i wont go into its detail right now.
As far as your situation go, With my experience I can tell you that you ve been doing stuff in a wrong manner. As apple has gone strict your implementation seems to break.
The only solution in my opinion is to shift every check in viewdidAppear.
If you wish to continue the way you were doing for ios7 earlier you might use this check:
if([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
{
// Code for ios 8 implementation
}
else
{
// Code for ios 7 implementation
}
Though i would reccomend you to avoid because wat u are aiming is perfectly achievable.
Also what you are doing can easily cause inconsistency in the navigation stack which can crash the application.

How to release memory from UIViewController after popping naviagtion

In my app, I am jumping navigation a bit, here is a rough outline of what my navigation does
Login -> Main Screen -> Settings
From settings, I want a logout that goes back to the login page. I do this like this:
UIViewController* requireController = [[[self navigationController] viewControllers] objectAtIndex:0];
[[self navigationController] popToViewController:requireController animated:YES];
This does return me to the login page. However, I would like to release some things in the Main Screen, for example I have a timer that runs a task every 10 seconds, which continues to run (I can see it in the logs). viewDidUnload obviously no longer gets called as of iOS6. viewDidDisapear also is not quite right because that will also get called when going into settings.
What should I do here to get rid of some tasks in my main view controller?
You can use the popToRootViewControllerAnimated: method to directly jump to the root view controller, in your case the Login controller. If you are not retaining the Main Screen or Settings controllers anywhere in your code, then moving to root view controller will invoke the dealloc method on the view controllers getting removed from navigation stack.
You can override the dealloc in your main screen controller and release the resources. Remember, if you are using ARC do not make a call to [super dealloc] as it will give error. But if you are managing memory manually make sure you add the call at the end of dealloc.
Hope that helps!
Set up a protocol on your logout view controller. Before you pop back to the main, call some method to reset the state of that view controller using the protocol (delegate) you created. On the main VC implement that method. AFTER telling the delegate to do whatever, then pop the login VC.
Here are the basics of this pattern.
http://iosdevelopertips.com/objective-c/the-basics-of-protocols-and-delegates.html
I would either use NSNotificationCenter or use the - (void)viewDidDisappear:(BOOL)animated method of UIViewController.

My iOS app complains about two-staged rotation but I'm not using it

I have to support down to iOS 4.3.
My app outputs in the console :
Using two-stage rotation animation. To use the smoother single-stage animation, this application must remove two-stage method implementations.
As far as I know I'm not using two-staged rotation. I just have this method in my view controllers :
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
What else should I check in order to fix that?
Edit:
More precisions: my app uses a UITabbarController subclass. When the app starts, it checks if a user is logged in and then initiates the controllers of the tabbar controller if it's the case. If there's no user logged in, a modal view is presented (from the tabbar controller) the prompt the user to login and the controllers of the tabbar controller aren't initialized (yet). The "two-staged rotation" error is shown only at that moment and the rotation doesn't work.
So to summarize, the problem happens in that situation:
The rootViewController of the main window is the tabbar controller
The tabbar controller is empty (there are no view controller in the tabs and there's no tab)
A view controller is modally presented from the tabbar controller
OK I found a solution.
It seems like the presented modal view won't rotate until the viewControllers property of the UITabBarController is initialized. Since the concerned modal view is actually for login, I don't want to display anything behind it until the user is logged in because the views intended to be hold by the tabbar controller depend on the fact that a user is logged in.
So now just before presenting the modal view, I initialize the tabbar controller with a single, empty UIViewController and I remove it when the modal view is dismissed (i.e. a user logged in).
Perhaps it seems like a hack but it works well. And even if I don't understand why, it doesn't seem completely illogical that the tabbar controller doesn't behave like we want until it is fully initialized.
If someone has a better solution or explanation, feel free to comment :)
There aren't many posts regarding this error, so I'll admit my own shortcomings for the benefit of the next person so focused on the trees they might miss the forest. I found a missing
[super viewWillAppear:animated];
call inside my sub.

performSegueWithIdentifier failing presentPopoverFromBarButtonItem Popovers cannot be presented from a view which does not have a window

I have a Master/Detail view which opens a popover view via storyboard segue. There is an add button on the navigation bar of the Master view controller which works fine.
I added an editing mode where the same popover is invoked by selecting a table cell in edit mode. It fails from the [self performSegueWithIdentifier:#"addQuery" sender:self]; statement. The viewDidLoad in the popover is invoked, but after that the exception is thrown.
I am not invoking presentPopoverFromBarButtonItem - it seems to be coming from the performSegueWithIdentifier.
There is no question that the Master View Controller has a window - a table cell for that view was clicked to start the whole process that is failing.
The popover is the beginning of a navigation controller sequence, which may be part of the problem. Everything is working fine when it really is invoked by the button, just trying to programmatically invoke it is failing.
I have tried changing the "sender" for the performSegueWithIdentifier to no avail.
I suspect the problem has to do with the segue not being invoked by a button, and I do not know how to fake that out.
Any ideas?
There appear to be some bugs in how ipad popover segue's work - see Wayne Hartman's blog post
A simple test revealed that viewWillAppear is being called after viewDidLoad.
I think I understand the problem... havent' worked out the solution yet.
The order in which the methods were called are...
[initiate segue]
viewDidLoad
prepareForSegue
viewWillAppear
I moved my initialization code to the viewWillAppear method - and it worked.
In general, I feel it may be a good idea to initialize within viewWillAppear instead of viewDidLoad anyway.
I have a similar issue:
I am using UIDocumentInteractionController to open a kaynote document in Kaynote app. I was using same code:
[docController presentOpenInMenuFromBarButtonItem:_actionBarButtonItem animated:YES];
Code above was opening popover from actiobBarButtonItem with options what app I would like to use to open my file. If I same thing from DetailViewController I get same error message as author of this thread: "Popovers cannot be presented from a view which does not have a window"
And I was able to find a quick solution for my issue . I am not sure if it will be relevant to yours. Instead of using "presentOpenInMenuFromBarButtonItem" I used "presentOpenInMenuFromRect" and thats it. You just need to define right place for popover to apear

Resources