Dismissing a modal and its caller - ios

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];
}];
}

Related

Dismissing a ViewController shown as Popover segue from within another Popover

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];

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.

Passing data to UIViewController when using modal

I have two view controllers, MainViewController and SecondViewController.
In my main view controller, I have data I pass to the second view controller like so:
-(IBAction)shareToSocial:(id)sender {
SecondViewController *view2 = (SecondViewsController *)[storyboard instantiateViewControllerWithIdentifier:#"PushToNext"];
view2.secTitle = self.MainTitle;
[self.navigationController setModalPresentationStyle: UIModalPresentationCurrentContext];
[self setModalPresentationStyle:UIModalPresentationCurrentContext];
}
Where both secTitle and MainTitle are NSStrings for each controller. From what I've read, by setting view2.secTitle to self.MainTitle, when SecondViewController pops in I my view2.secTitle will have the same value as MainTitle. So if MainTitle is "Hello World" then I open secondViewController, secTitle would also be "Hello World". However, I've been getting null with secTitle even though I am setting the data for it.
If I use pushViewController instead of modal, the values are passed between the two controllers fine. So I'm not sure if I'm doing anything wrong here exactly.
I've also tried using [view2 setSecTitle:MainTitle] to no avail.
There's not much code on my SecondViewController but here's how it looks:
-(IBAction)showMessage:id(sender){
NSLog(#"test: %#", self.secTitle);
}
-(IBAction)closeMessage:id(sender){
[self dismissViewControllerAnimated:YES completion:nil];
}
Also, I've noticed that when I used dismissViewControllerAnimated on SecondViewController to close it and go back to the MainViewController, a black empty screen shows for a few seconds before showing the Main View and touch events on MainViewController is not detected anymore.
Please help.
Could you use performSegueWithIdentifier and prepareForSegue? If you've got your views in your storyboard and set up a segue between them, given it an identifier and set the style to modal you should be able to do this:
-(IBAction)shareToSocial:(id)sender {
[self performSegueWithIdentifier:#"modelToNextSegue" sender:self];
}
Then in prepareForSegue method you pass your values:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
SecondViewController *view2 = [segue destinationViewController];
view2.secTitle = self.MainTitle;
}
Try this:
SecondViewController *view2 = (SecondViewsController *)[storyboard instantiateViewControllerWithIdentifier:#"PushToNext"];
view2.secTitle = self.MainTitle;
UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:view2];
[nav setModalPresentationStyle: UIModalPresentationCurrentContext];
[self setModalPresentationStyle:UIModalPresentationCurrentContext];

pushing UIViewController without Navigation Controller

I was using that method for pushing another UIViewController in if condition :
initViewController *MW = [initViewController alloc];
MW = [self.storyboard instantiateViewControllerWithIdentifier:#"init"];
[self.navigationController pushViewController:MW animated:YES];
Now i've changed my architecture and i just removed my Navigation controller. That's why , these steps will not help me any longer due to no NavigationController exist. Now how can i push another ViewController without pressing button -(IBAction) . I just wanted to use it in Storyboard.
Regards.
FYI : without UINavigationController You can't push to another Controller . You can produce a Controller using
PresentViewController
[self presentViewController:objPortrit animated:YES completion:^{ }];
For Ex:
initViewController *MW = [initViewController alloc];
MW = [self.storyboard instantiateViewControllerWithIdentifier:#"init"];
[self presentViewController:MW animated:YES completion:^{
}];
EDIT:
For your comment :
after producing the ViewController with PresentViewController, You need to dismiss it to produce another ViewController .
You can get over from this , something like this in their respective ViewController Use his method. :
-(void)viewDidDisappear {
[self dismissModalViewControllerAnimated:YES];
}

Resources