I am facing a strange problem -
I have a navigation Controller(NV) which presents the Root Controller(NVV1). NVV1 pushes another view controller on top of it called NVV2.
NVV2 presents a modal controller (MV1). MV1 presents another modal controller (MV2).
On a user action in MV2, it dismisses itself by calling self.presentingViewController dimissViewControllerAnimated. After dismiss, MV2 calls a method of MV1. That method in MV1 does a network call and now (tries to) dismiss itself by calling self.presentingViewController dismissViewControllerAnimated to show NVV2 and calls a method of NVV2 as well.
The problem I am facing MV1 is not getting dismissed. Am I missing something? The error that I am getting -
attempt to dismiss modal view controller whose view does not currently appear. self = <MV1: 0xaebbfc0> modalViewController = <UINavigationController: 0x1976ac10>
I think your problem is that you are calling dismissViewControllerAnimated on the presentingViewController instead of self. Normally, this would be fine, but if you look at the Discussion section for dismissViewControllerAnimated a presented view controller will automatically forward to it's presenting view controller.
In this case, you may have found a minor bug where multiple presented view controllers don't check if they are also a presenting view controller before forwarding the message. If you change the calls to self, you might be able to prevent MV1 from forwarding the message to NVV2.
Seems like you're attempting to dismiss the MV1 view before MV2 has been dismissed. Either try adding a slight delay before dismissing MV2:
[self performSelector: #selector(dismissMV1) withObject:nil afterDelay: 0.1f];
Or better yet, you could try calling the dismissMV1 method from the MV2 method by using a completion block (available in iOS 5.0 and above) to trigger dismissMV1 once the dismissal is complete:
[self.presentingViewController dismissViewControllerAnimated:YES completion: ^{
[self.presentingViewController dismissMV1];
}];
I solved this problem by directly calling dismissViewController on NVV2, that is in MV2, I called [self.presentingViewController.presentingViewController dismissControllerAnimated].
That solved my problem in this case as I wanted to move from MV2 to NVV2 directly. Ideally, I would have liked to move to MV1, do some network operation and indicate the user that a network operation is ongoing on MV1, but in my case showing that on NVV2 also works fine.
The original question, though, still remains unanswered - how to move from MV2 to MV1 and then to NVV2 directly. I think this might be a minor bug because according to the docs, if you call dimissControllerAnimated on a view it sends the message to its parent as well - essentially, the call going all the way to NVV2 which is undesirable. I do not yet have a solution for this.
Related
I have a a VC structure like this
UIPageViewController -> detailViewController -> popoverviewcontroller
The popoverviewcontroller is dismissed using an unwind segue, bringing us back to the detailviewcontroller
Now, after the popover is done being dismissed, I would like to refresh the pages on the pagecontroller, since the action the user takes has changed the data.
I would also like to display an alert notifying the user about whether they were successful.
So I tried putting this code in the pageViewcontroller
- (IBAction) unwindFromPopup:(UIStoryboardSegue*)unwindSegue{
[self refreshPages];
UIAlertController * alertController = [UIAlertController alertControllerWithTitle:#"alert" message:#"this should appear" preferredStyle:UIAlertControllerStyleAlert];
[self presentViewController:alertController animated:YES completion:nil];
}
I tried moving the above code to the detail view controller instead, but I am getting no result from this. No error or anything, just a complete lack of alert. I put a breakpoint in the segue, and the code gets called. But no alert.
I thought of putting the code in one of the respective viewDidAppear methods, but for some reason viewDidAppear does not get called for either the pageviewcontroller or the detailview controller after I dismiss the popup.
So at this point I have no idea how to make this alert appear.
Do I need to post my full code, or is my problem apparent with the details I've included?
Thanks - based on your comment ... long ago in a distant version of iOS I performed all the possible segues and noted what gets called when and have a table of that that I based my answer on. I must admit, nowadays I get most done using the presentation controller delegate.
Anyhow, to reply to your question, when you pop or modally present a controller, the controller that is being presented will message beingPresented and beingDismissed when it is done and you might be able to use this for what you are trying to do.
When you push a controller it will message isMovingToParentViewController when shown and isMovingFromParentViewController when dismissed, again in the controller being presented.
Back to a pop ... it will message prepareForSegue in the presenting VC and viewWillAppear and viewDidAppear in the presented VC and, when dismissing, will message only viewWillDisappear and viewDidDisappear in the presented VC, thus your problem. At least it will also message beingDismissed as mentioned and if you can use that I am really glad for you.
So my setup is very simple I have:
Controller A
Controller B
Controller A segue's to Controller B, and it's a modal display.
When Controller B's viewDidLoad fires, I instantiate the NSURLConnection with request and start immediately.
If there's no network connection, then the didFailWithError gets fired.
From within here, i call dismissViewController, but when I do, I receive:
"Attempt to dismiss from view controller while a presentation or dismiss is in progress!"
What could possibly be causing this error?
I also have a button on Controller B, that when clicked also does the dismissViewController, but it does it correctly.
Could this be a race condition where Controller B has not finished animating/displaying once it hits viewDidLoad, and the URL connection spawning and failing immediately?
If this is the case, what's the correct way to fix this?
Thanks.
You can't dismiss a view controller until it has been completely presented. If you're still in viewDidLoad, this certainly isn't going to be the case, and I think this mostly will not be the case in viewWillAppear. viewDidAppear is the earliest possible state you can guarantee the view controller's presentation is complete and ready to be dismissed.
With that said... it's better from a UI perspective to instead try to start the NSURLConnection in the background and if you have a good network connection, then you present view controller B, and if not, simply never present it, rather than present and immediately dismiss.
Seems to be race condition. Can you try doing this in failToLoad delegate and see?
//Goes into the failed delegate
dispatch_async(dispatch_get_main_queue(), ^{
//dismiss the View Controller here??
});
We have a container view controller and want to be able to call "PerformSelector" on one of the "sub" view controllers in that container, right after starting a transition, i.e.
[self navigateSubViewControllerTo:newSubViewController];
... some time later, elsewhere in the stack, a selector will be performed on the top visible VC
[subViewController performSelector:#selector(foo)];
The call to transitionFromViewController happens in navigateSubViewController.Unfortunately, since transitionFromViewController happens asynchronously, we are finding that the performSelector call gets applied to the "before" sub view controller, not newSubViewController. I.e. it is happening before the transition happens.
Any thoughts on how to have performSelect not happen until the sub view controller transition happens?
You can just call performSelector in the completion block of
transitionFromViewController:toViewController:duration:options:animations:completion:
UIViewController provides a callback beginAppearanceTransition:animated: which is exactly for this purpose. Simply implement it in your subviewcontrollers and your good to go ;)
I have a modal view which is launched from the current view controller, as
[self presentModalViewCOntroller:modalViewController animated:TRUE];
The modal view controller dismisses itself when someone hits a button.
[self dismissModalViewControllerAnimated:TRUE];
A couple of screens later, I attempt to swap the root view within the window. I do this all the time with no trouble. But in a certain case, when switching the one view within the window, the picker delegate method is being called on the modal view controller even thought it was dismissed a while ago.
This is very strange because the modal view controller is usually deallocated when dismissModalViewController is called.
Why is a view from the modal view controller being invoked?
It appears that someone, probably the window still has a reference. Are you supposed to do something else in addition to dismissModalViewController?
Thanks
DismissModalViewController should be enough. It does seem like you have a problem with some reference hanging around that you don't intend. Without seeing more code, I can't point to anything specific.
I need to save my data by calling a method I already have when a viewController is popped using the back button created by the UINavigationController.
Is there a way to get a delegate callback or a notification I didn't see anything in the documentation?
In your viewWillDisappear method, you can check the property:
[self isMovingFromParentViewController]
to find out if the view is disappearing as a result of being popped off the stack or not.
You will be notified that the view will be disappearing, with the view controller method viewWillDisappear:, however, this will be called each time the view is moved offscreen, whether that means the controller is popped or a new controller is pushed, or whatever else may cause your view to disappear.
Perhaps a better design would be to save your data in your controllers dealloc method. Normally, a navigation controller is the owner of a view pushed into it's stack, so popping it usually causes it to deallocate. This isn't always the case though and depends on how you've written your app.