Dimiss modal View Controller in background - ios

I got View A present -> Modal View B, B present -> Modal View C Modal View D -> Modal View E . It's a main View A and then succession of Modal view.
So each time I present new modal C or D I want to dismiss the previous one (In this way when I close the new presented Modal It will show me The Main view A always not the previous Modal).
If I'm in B and I want to present C then I have to present C first
then In background I dismiss B.
If I'm in C and I want to present E then I present E first and then
dismiss C in background
I've tried this code :
if (_openNextView) {
if ([[NSUserDefaults standardUserDefaults] stringForKey:#"generatedCode"]) {
NSLog(#"generated Code %# : ", [[NSUserDefaults standardUserDefaults] stringForKey:#"generatedCode"]);
NSLog(#"phone Number %# : ", [[NSUserDefaults standardUserDefaults] stringForKey:#"phoneNumber"]);
// Present C View
RegisterSecondViewController *registerSecond = [[RegisterSecondViewController alloc] initWithNibName:#"RegisterSecondViewController" bundle:nil];
//[self presentNatGeoViewController:registerSecond];
[self presentViewController:registerSecond animated:YES completion:nil];
} else {
RegisterFirstViewController *registerFirst = [[RegisterFirstViewController alloc] initWithNibName:#"RegisterFirstViewController" bundle:nil];
//present D view
//[self presentNatGeoViewController:registerFirst];
[self presentViewController:registerFirst animated:YES completion:nil];
}
// Dismiss privious View (the current view before presenting new one)
[self.parentViewController dismissViewControllerAnimated:YES completion:nil];
Also I've o add this code in the new presented Modal view
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
But didn't worked and the view is not dismissed

I'd modally present a UINavigationController and use it to present the content views. Then, when you currently present new modal views instead you would just set the viewControllers (animated) of the navigation controller.
This will give you an animated view of progression through your setup process but won't allow the user to go back and will deallocate the VCs when they're complete.

UIViewController has a property named presentedViewController :
presentedViewController
Property The view controller that is
presented by this view controller, or one of its ancestors in the view
controller hierarchy. (read-only)
This means, you can only present one modal controller at a given time. In order to present another one, you need to dismiss the current one first.
If you want to have a custom navigation, I recommend you to create a custom container view controller since the normal modal presentation won't fit your use case. Here is a guide from Apple.

You can dismiss the current vc after that presenting a new vc. Try this:
[self presentViewController:vc animated:YES completion:^{
if (self.presentingViewController != nil) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self dismissViewControllerAnimated:NO completion:nil];
});
}
}];

Related

How to dismiss a viewcontroller within another ViewController?

Let say I have view controllers named A,B,C,D,E I open theseViewConrollersone after the other in presentModel way. When I am in ViewController E I want to open another ViewController F in presentModel way. In that F ViewController I have a back button. When I click on that It should dismiss the F and show the A ViewController. But now when it dismissse shows E. How can I dismiss all other viewcontrollers except the A when I click the back button of F
Please help me. Thanks
UPDATED
-(IBAction)dismisthis:(id)sender{
UIViewController *dismissingViewController = self.presentingViewController;
while (dismissingViewController.presentingViewController != nil && [dismissingViewController isKindOfClass:[FrontViewController class]]) {
dismissingViewController = self.presentingViewController;
}
[dismissingViewController dismissViewControllerAnimated:NO completion:NULL];
If you dismiss a view controller that is presenting another view controller, the entire hierarchy is dismissed. It doesn't matter how many levels there are. So all you have to do is find your view controller A and tell it to dismiss its presented view controller.
If A is always the bottom of the heap, you can use a simple loop to find it:
UIViewController *dismissingViewController = self.presentingViewController;
while (dismissingViewController.presentingViewController != nil) {
dismissingViewController = self.presentingViewController;
}
[dismissingViewController dismissViewControllerAnimated:YES
completion:NULL];
What about sending Notification using NSNotificationCenter from F and A will listen to it. Once A receives the notification, it will call dismissViewController which I think will dismiss all.
At this moment i can think of three solutions
You should keep track of all viewControllers in a stackObject(which is an array) in Appdelegate. When you want it access this get these array and dismiss all view controler objetcs.
Each viewcontroller you can observer for a NSNotification which will listen for notification. When you need it in "F" just post the notification and this notification will dismiss the eviewcontrollers
Go for NavigationController so that you can push to rootviewcontroller
Try this:
UIViewController *rootVC = [UIApplication sharedApplication].delegate.window.rootViewController;
[rootVC dismissViewControllerAnimated:YES completion:nil];
// One-liner
// [[UIApplication sharedApplication].delegate.window.rootViewController dismissViewControllerAnimated:YES completion:nil];
Make A as rootViewController and use below code on Back button of "F":
[[[[UIApplication sharedApplication] keyWindow] rootViewController] dismissViewControllerAnimated:true completion:nil];

Dismissing previously presented modal view controller before currently visible one

I have 2 UIViewController's presented with [self presentViewController:viewController animated:YES completion:nil];, I want to dismiss the first one of them, without animation (It's not visible to the user anyway) and when the second one (currently visible) will be dismissed, the user will see the parent view controller who present them both.
- Parent
- First -> Dismiss first without animation
- Second -> Dismiss second with animation
How can I do that?
With your current view controller hierarchy if first view controller will be dismissed it will dismiss second view controller automatically. If you don't want that behaviour than make parent present second view controller. You can do that from first view controller by using [self.presentingViewController presentViewController:secondViewController animated:YES completion:nil]
Why do you want to do this?
You should do it like this for cleaner view hierarchy and better user experience:
Present first view controller :
[self presentViewController:viewController1 animated:YES completion:nil];
Dismiss first & present second view controller :
__weak MyViewController *aBlockSelf = self;
[self dismissViewControllerAnimated:YES completion:^{
[aBlockSelf presentViewController:viewController2 animated:YES completion:nil];
}];

Modal View Controller, dismiss and pop back to view controller

Im trying to dismiss a ModaViewController called C, back to A. The ModalViewController is presented in B. Therefore the Navigation flow is A->B - (present ModalView) -> C. I am able to dismiss the ModalViewController back to B, but I am unable to pop back to A in the completion bracket. This is the code I have tried:
[self dismissViewControllerAnimated:YES completion:^{
[self.navigationController popToViewController:[[self.navigationController viewControllers] objectAtIndex:0] animated:YES];
}];
The ModalViewController is dismissed but does not pop back to A. I call this block of code in an IBAction.
Any advice?
On a second note, when I dismiss the ModalViewController all my UIPickers in Controller B are empty/ deallocated. I am using ARC as well.
The problem with your code is that self.navigationController will be nil. If you have a controller (A) embedded in a navigation controller, and that controller pushes to another controller (B) which then presents your last controller (C), then you need to do something like this,
-(IBAction)dismissToBThenPop:(id)sender {
UINavigationController *nav = (UINavigationController *)self.presentingViewController;
[self dismissViewControllerAnimated:YES completion:^{
[nav popViewControllerAnimated:YES];
}];
}
Even though you present C from B, the actual presentingViewController will be the navigation controller. This code will dismiss C then pop B, but you will see B for an instant before ut pops back to A. If you don't want to see this, then you should use an unwind segue to go directly back to A from C.
Your second problem about the pickers being empty and deallocated should not be happening under the scenario that you say you have. You will have to provide more information about what you're doing in B to solve that problem.
Create a protocol in ModalViewController, let's say ModalViewControllerDelegate with a method -(void)dismissTheModal, and make B implement this protocol. Before showing the ModalViewController, do modalViewController.delegate = self. When you're IBAction is called, do [self.delegate dismissTheModal], and in controller B you should do :
-(void)dismissTheModal {
[self dismissViewControllerAnimated:YES completion:^{
[self popViewController];
}];

Generic way of Popping & Dismissing View Controllers

I am building an application in which, i have 3 ViewControllers.
Also i have created custom navigation controller.
A-ViewController -> This contains 2 buttons, 1st button is for opening B-ViewController & 2nd button is for C-ViewController.
From 1st button, I am using pushviewcontroller for B-ViewController, means B-ViewController is pushed from A-ViewController.
From 2nd button C-ViewController is presented using presentviewcontroller.
Now on pressing back button in both B & C ViewControllers, I have to use pop view controller in B-ViewController & dismiss in C-ViewController.
As currently i know the pages, but there should be a generic solution.
Is there any way to identify whether current navigation controller is pushed or popped.
As there are some pages which can be pushed or presented, but i dont want to set any bool variable. I need use the support from apple.
NSArray *arrViewControllers = [[AppDelegate sharedInstance].navigationController viewControllers];
NSLog(#"[arrViewControllers count] = %d",[arrViewControllers count]);
I am using the above code for fetching the list of view controllers in the stack.
But i am not able to identify whether it is pushed or presented.
Can anybody help me in this ?
You can check if your view controller exists in the navigation view controller's stack. Simply check it as
if([self.navigationController topViewController] == self){
//VC is the top most view controller
[self.navigationController popViewControllerAnimated:YES];
}else{
//You can put some checks here to be dead sure its a modally presented view controller
[self dismissViewControllerAnimated:YES completion:nil];
}
This is the code for Finding the navigation controller is presented or pushed from previous controller.
NSArray *arrViewControllers = [[AppDelegate sharedInstance].navigationController viewControllers];
UIViewController *viewController = [arrViewControllers lastObject];
if (viewController.presentedViewController)
{
[self dismissViewControllerAnimated:YES completion:nil];
}
else{
[self.navigationController popViewControllerAnimated:YES];
}

popToViewController is not working "Tried to pop to a view controller that doesn't exist"

I am trying to use popToViewController and it I keep getting the error "Tried to pop to a view controller that doesn't exist"?
I am in a Settings view and when the user clicks "Sign Out" I dismiss the Settings VC and segue back to the mainView where an unwind segue method is called. In the unwind segue method I call the following.
-(IBAction)endSettingsViaLogout:(UIStoryboardSegue *)segue {
//[self performSegueWithIdentifier:#"mainToLoginSegue" sender:self];
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];
//[self.navigationController popViewControllerAnimated:YES];
DLog(#"User finished with search");
}
When the poptoVC is called I get the "Tried to pop to a view controller that doesn't exist".
I NSLog the self.navigationController.viewControllers and I can see the stack of VC's and the one I want to pop to is in there?
/// UPDATE //////
Ok here is what I have found. If my segue to Settings is a regular "push" segue then the code works and I get popped back to where I want. If I do a custom segue, having come from the left side of the screen then it stops working. Even with the custom segue the self.navigationcontroller.viewcontrollers shows its in the stack. So why can't I pop back to it? Or how can I pop back to it with the custom segue?
Do I get the view controller hierarchy right?
On your basis UINavigationViewController you have set a main view controller as root view controller.
Then a settings view controller has been pushed upon this.
Via "Sign out" the settings view controller is being segued back to the main view controller.
If so, you are actually trying pop "back" to a view controller, that does not exist, since you already have reached the root view controller of your navigation controller stack. In this case, all previously initialized controllers have been popped from the stack and you would have to reinitialize and push the desired view controller.
If I am missing some important point, it would be helpful, if you would describe your actual view controller stack at the moment the "Sign out" option is available. Furthermore, what exactly is printed on the console if you log the self.navigationController.viewControllers array?
well that basically tells objectatindex 1 does not exist:
things to try:
objectatindex:0
or
nslog(#"%d",[[self.navigationController.viewControllers]count]);//add it before the popline and see if it works
if its the topview controller then try below:
[navigationController topViewController] instead
NSArray *viewControllers = [[self navigationController] viewControllers];
for( int i=0;i<[viewControllers count];i++)
{
id obj=[viewControllers objectAtIndex:[viewControllers count]-i-1];
if([obj isKindOfClass:[OrderCheckOutViewController class]]){
[[self navigationController] popToViewController:obj animated:YES];
return;
}
}
you can use the snippet to pop out to targetVC's next viewController in the navigationController's stack.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
BOOL findIt = NO;
UIViewController *targetVC = nil;
for (UIViewController *subVC in self.navigationController.viewControllers) {
if (findIt) {
break;
}
if (subVC == xxx) {
findIt = YES;
}else{
targetVC = subVC;
}
}
[self.navigationController popToViewController:targetVC animated:NO];
});

Resources