Dismissing a ViewController shown as Popover segue from within another Popover - ios

My storyboard is using a UINavigationController(VC0), I segue using a UIPopover to load a new UIViewController (VC1). From this new UIViewController, I am popping to a new UIViewController (VC2). When I attempt to close this newest popover, I am being quit back to the UINavigationController.
The code that I'm using in VC0 to show the VC1 as popover, VC1 to VC2 is the same code (different identifier though):
[self performSegueWithIdentifier:#"titlePopover" sender:self];
The code that I'm using in VC2 to dismiss the popover is:
UIViewController *vc = [self presentingViewController];
[self dismissViewControllerAnimated:YES completion:^{
[[vc presentingViewController] dismissViewControllerAnimated:YES completion:nil];
}];
What it does is dismisses VC2, shows VC1 for a split second, then dismisses VC1 and goes back to the VC0. I want to be able to dismiss VC2 only, so that I'm on VC1

If you performing sugue modally, use code navigate back:
[self dismissViewControllerAnimated:YES completion:nil];
If performing push segue, use code to navigate back:
[self.navigationController popViewControllerAnimated:YES];

Related

Attempt to dismiss from view controller (UIModalViewController) while a presentation or dismiss is in progress

I have just started working on ios.
I created a Modalviewcontroller (VC1) and presented another modalViewcontroller (VC2).
There is a button (dismiss) on VC2 which will have to dismiss both viewcontrollers.
The way i know is call :-
[self dismissViewControllerAnimated:YES completion:nil];
in VC2
then call the same in VC1
So i created a delegate which tells me if dismiss is clicked in VC2.
so when dismiss is clicked:-
i call
[self dismissViewControllerAnimated:YES completion:nil];
in VC2
then that delegate method takes me to VC1
where I again call
[self dismissViewControllerAnimated:YES completion:nil];
This method was perfectly working till i was using the app in ios9
when i shifted to ios7 i started getting the warning and VC1 was not getting dismissed.
Please let me know why is this happening.
So the part which works for me as told in comments.
[self.presentedViewController dismissViewControllerAnimated:YES completion:^{
[self dismissViewControllerAnimated:YES completion:nil];
}];
So, error tells you exactly what happened. You trying to dismiss VC1, while your VC2 dismissing. By putting dismissViewControllerAnimated into delegate method does not guarantee that VC1 will be dismissed before, instead of that you should call your [self dismissViewControllerAnimated:YES completion:nil]; in completion block after first dismiss, so your code will look like that:
[self dismissViewControllerAnimated:YES
completion:^{
[self dismissViewControllerAnimated:YES completion:nil];
}];

push view controller after dismissing presented view controller

I have this navigation stack
RootVC ---> VC1 --> (presenting)-> ModalVC
and I have VC2 (not in navigation stack).
When presenting ModalVC, I want to click on button in my ModalVC to dismiss ModalVC, then push VC2 into the navigation stack after VC1 at one click. It should look like this:
RootVC ---> VC1 ---> VC2
I tried a lot of methods to make it, but pushing event fire only, when I return to my RootVC.
I tried to make it with delegates:
In ModalVC on click:
[self dismissViewControllerAnimated:YES completion:^{
if ([self.delegate respondsToSelector:#selector(dismissAndPush:)]) {
[self.delegate performSelector:#selector(dismissAndPush:) withObject:VC2];
}
}];
In VC1:
- (void)dismissAndPush:(UIViewController *)vc {
[self.navigationController pushViewController:vc animated:NO];
}
Please help to understand this behavior. Where is my mistake?
From Apple Documentation:
The presenting view controller is responsible for dismissing the view
controller it presented.
So, VC1 should be dismissing the ModalVC, try to do this
ModalVC on click:
if ([self.delegate respondsToSelector:#selector(dismissAndPush:)]) {
[self.delegate performSelector:#selector(dismissAndPush:) withObject:VC2];
}
In VC1:
- (void)dismissAndPush:(UIViewController *)vc {
[self dismissViewControllerAnimated:YES completion:^{
[self.navigationController pushViewController:vc animated:NO];
}];
}
Error has been in other. If Im right understand: some animations before dismissing presented view controller are blocking animations in navigation stack. I solved this problem with 2 ways:
1) deleteting or set right animations before dismissing
2) use setViewControllers in navigation controller (I select it)
- (void)dismissAndPush:(UIViewController *)vc {
[self dismissViewControllerAnimated:NO completion:^{
NSMutableArray *mutableControllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
NSArray *controllers = [mutableControllers arrayByAddingObject:vc];
[self.navigationController setViewControllers:controllers animated:NO];
}];
}

Pop view controller after dismiss

I have a navigation controller A on which i push the view controller B. From B i present modally the view controller C. I need to dismiss C and pop B at the same time. I would like to that in sequence, keeping the dismiss animation first and then the pop animation from B to A. I tried without success this code:
[self dismissViewControllerAnimated:YES completion:^{
[self.presentingViewController.navigationController popViewControllerAnimated:YES];
}];
Any suggestion on how can i achieve this?
If you are writing in C viewcontoller then :
UIViewController *pvc = self.presentingViewController;
UINavigationController *navController = [pvc isKindOfClass:[UINavigationController class]] ? (UINavigationController *)pvc : pvc.navigationController;
[self dismissViewControllerAnimated:YES completion:^{
[navController popViewControllerAnimated:YES];
}];
or if in B view controller
[self.presentedViewController dismissViewControllerAnimated:YES completion:^{
[self.navigationController popViewControllerAnimated:YES];
}];
I had tried popping two times in succession before but not dismissing one and popping another one. You can try what I did and see if it works for you.
In Subview B:
- (void)subViewCController:(SubViewCController *)controller didSelectID:(NSNumber *)theID
{
// do something with theID...
// for my case, I popped
// [self.navigationController popViewControllerAnimated:YES];
// for your case
[self dismissViewControllerAnimated:YES completion:nil];
// Somehow, adding a small delay works for me and the animation is just nice
[self performSelector:#selector(backToSubViewA) withObject:nil afterDelay:0.6];
}
- (void)backToSubViewA
{
[self.navigationController popViewControllerAnimated:YES];
}
Are you using storyboards and segues? If so, you can use unwind segue:
In your first view controller (the one you want to jump back to, not the one you're returning from), create an unwind segue action:
- (IBAction)gotoMainMenu:(UIStoryboardSegue *)segue
{
// if you need to do anything when you return to the main menu, do it here
}
Then in storyboard, you can create a segue from the "dismiss" button to the exit outlet icon () in the bar above the scene, and you'll see main menu listed there.

Dismissing a modal and its caller

Sorry but none of the similar questions provide a working solution.
New single-view project.
Storyboard actions:
VC1 is embedded in nav-controller
a button in VC1 activates a push seque into VC2
a button in VC2 activates a modal segue into VC3
VC3 has a button, and a UIViewController-subclass to handle the button action
Problem statement: How can I have the button of the modal VC3 close both VC3 and VC2 at once, taking me back to VC1?
This did not work - takes me to VC2 only:
- (IBAction)dismissPressed:(id)sender {
UINavigationController *myNavController = [self navigationController];
[self dismissViewControllerAnimated:NO completion:^{
[myNavController popToViewController:[myNavController.viewControllers objectAtIndex:1] animated:NO];
}];
}
Thank you very much!
Ok guys, here is the solution:
If you can limit yourself to ios6+, the right answer is to make the button an unwind segue as described in What are Unwind segues for and how do you use them?
But if you are like me and must comply to ios5, the solution goes likes this:
In the VC who calls the modal segue declare a delegate like this:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"segueModalVC"]) {
ModalVC *vc = [segue destinationViewController];
vc.prevVC = self;
}
}
You will have to #import the header for the destination modal VC.
Now in the modal VC itself add a property to hold the previous VC like this:
#property (weak, nonatomic) UIViewController *prevVC;
And now the button's action for popping both VCs should look like this:
- (IBAction)pressedMainMenu:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
[[self.prevVC navigationController] popViewControllerAnimated:NO];
}
[EDITED to show the final version which works perfectly and can do flip animation from VC3 back to VC1. Took me hours to finalize this but now you can enjoy it for free :) ]
IMHO the fact that the modal VC has no 'native' method to access its preceding VC is a design bug.
Use self.presentingViewController to find out the "previous view controller".
You do not need to pass the previous view controller through the property.
- (IBAction)dismissPressed:(id)sender {
UIViewController *presentingViewController = self.presentingViewController;
[self dismissViewControllerAnimated:NO completion:^{
[self.presentingViewController popViewControllerAnimated:TES];
}];
}
Try to Use popToRootViewControllerAnimated method to go back VC1.
- (IBAction)dismissPressed:(id)sender {
UINavigationController *myNavController = [self navigationController];
[self dismissViewControllerAnimated:NO completion:^{
[myNavController popToRootViewControllerAnimated: NO];
}];
}

Ugh, dismissing uiviewcontrollers and imagePickerController

Here's my set-up:
Nav Controller: ViewcontrollerA -> ViewControllerB
ViewcontrollerB displays a imagePickerController, setting itself as Delegate
If the user hits CANCEL from the UIImagePicker, I was to dismiss ViewControllerB completely, popping the user back to ViewControllerA.
In:
-(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
I have called:
[self dismissViewControllerAnimated:YES completion:nil];
and
[[self parentViewController] dismissViewControllerAnimated:YES completion:nil];
and
[[[self parentViewController] parentViewController] dismissViewControllerAnimated:YES completion:nil];
No luck, ViewControllerB still hangs around.
How to I completely dismiss B programmatically from itself?
Is ViewControllerA presenting ViewControllerB or is ViewControllerB being pushed via your navigation controller? It sounds like it's being pushed. In this case I would try
[[self navigationController] popViewControllerAnimated:YES];
Edit for clarity:
You do want to dismiss the image picker the way you already are. You want to use popViewControllerAnimated to get rid of ViewControllerB.
The presenting view controller is responsible for dismissing the view
controller it presented. If you call this method on the presented view
controller itself, it automatically forwards the message to the
presenting view controller.
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];

Resources