I have a firstViewController that I display with
[self.navigationController pushViewController:firstViewController animated:true];
Then when a button in this first VC is clicked, I present a second one:
[self presentViewController:secondViewController animated:true completion:nil];
And from this second one I present the third VC as a modal:
[thirdVC modalPresentationStyle];
[thirdVC setModalPresentationStyle:UIModalPresentationOverCurrentContext];
[self presentViewController:thirdVC animated:true completion:nil];
The problem is that I want to display the firstVC when the validate button of the last VC is clicked. So I have to dismiss the third and the second one. I tried this in the validateAction (in the third VC):
[[self parentViewController] dismissViewControllerAnimated:true completion:nil];
[self dismissViewControllerAnimated:true completion:nil];
But the result is that the thirdVC (the modal one) is dismissed, and from the debugger I can see that the firstVC is covered by the secondVC.
How can I dismiss the second and the third VC at the same time in order to return to the first one?
NOTE: I want to push the firstVC after the dismiss of the others because I have to reload the data (in the fristVC viewDidLoad)
From Apple's Documentation:
The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, UIKit asks the presenting view controller to handle the dismissal.
In other words, the third viewController asks the second viewController to dismiss its presented viewController, which is the third one. Then the third viewController tries to dismiss itself, causing UIKit to ask the second viewController to dismiss the third one once more.
You will have to go one layer up and ask the first viewController to dismiss its presented view controller.
It is good practice that the third viewController does not know anything about the presentation hierarchy. You can use delegation to tell the first viewController that the task is finished. An exit segue is a very nice alternative if you use storyboards.
You only have to call -dismissViewControllerAnimated:completion: once if you address the first viewController:
If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack.
Related
I have a presenting view controller that presents a modal like VC, then when the user is done with that modal, I want to transition to another modal, but calling:
// we are done.
[self.presentingViewController dismissViewControllerAnimated:NO completion:nil];
if (self.showOtherContoller)
{
UIViewController* aVC = [[UIViewController alloc] initWithNibName:#"someNib" bundle:nil];
[self.presentingViewController aVC animated:NO completion:nil];
}
works, but the presenting base view controller shows for about a second, so it looks all flashy.
Basically I want to replace one view controller with another, not temporarily drop to the presenting view controller.
Thanks for any tips.
Your modal transition style is CoverVertical. The first modal slides back down when dismissed, exposing its presentingViewController, then the second modal slides up, hiding the presentingViewController.
You want to change the transition so it appears that the first modal is replaced by the second modal.
Change both view controllers' modal transition styles to CrossDissolve before dismissing the first and presenting the second. Before dismissing the second, change it to CoverVertical, so it slides down to match the first modal's slide up transition.
WLINewPostViewController *newPostViewController = [[WLINewPostViewController alloc] initWithNibName:#"WLINewPostViewController" bundle:nil];
UINavigationController *newPostNavigationController = [[UINavigationController alloc] initWithRootViewController:newPostViewController];
newPostNavigationController.navigationBar.translucent = NO;
[tabBarController presentViewController:newPostNavigationController animated:YES completion:nil];
So I just simply push a new UIViewController.
Then after it posts the server callback calls a method with this code from the WLINewPostViewController.m:
[self dismissViewControllerAnimated:YES completion:^{
NSLog(#"Completed");
}];
[[self navigationController] popViewControllerAnimated:YES];
if (self == self.navigationController.visibleViewController){
NSLog(#"self = visibile");
}
if (self == self.presentingViewController.presentingViewController){
NSLog(#"self = presenting");
}
}
I tried a bunch of different things and none work.
I am relatively new to Xcode but after trying
[self dismissViewControllerAnimated:YES completion]
[self.navigationController popViewControllerAnimated:YES]
[self.navigationController.visibleViewController.presentedViewController dismissViewControllerAnimated:YES completion:nil];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
and every other possibility, I am officially stumped. The WLINewPostViewController still won't dismiss.
It Logs out "self = visible"
Let me illustrate what you are trying to do
You have a navigation controller with Controller A.
Here you are trying to present another Controller B from Controller A.
Now when you get a callback from the server, you should call dismissViewControllerAnimated from Controller B to dismiss itself.
So after dismissViewControllerAnimated:completion: method call, the Controller B will be dismissed and Controller A will be shown automatically. Now you do not need to call popViewControllerAnimated: in completion block again as there is no other Controller in navigation controller to load.
If you have different use case, let me know I can provide solution.
You are presenting a view over navigationbar instead of pushing it over navigationbar.
When push you pop. When you present you dismiss. So instead of popViewControllerAnimated you need to use dismissViewControllerAnimated:completion
dismiss behaves differently depending on the receiver. From the docs:
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.
If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack. When this happens, only the top-most view is dismissed in an animated fashion; any intermediate view controllers are simply removed from the stack. The top-most view is dismissed using its modal transition style, which may differ from the styles used by other view controllers lower in the stack.
In short, if the vc on top calls it on itself, it dismisses itself. Anywhere else on the stack dismisses to that point, animating only the topmost vc.
What's extra confusing (for you and many others) is that the navigation vc has a stack too, and your problem is complicated further by presenting an navigation vc atop a tab-bar vc.
So what to do? The question is unclear about which vc is the receiver in the posted code (who is self in that snippet?). The text implies that self is a vc on the stack of the presented navigation vc, like...
TabBarVC --- presents ---> NavVC
| |
| --- viewControllers stack = rootVC, vc1
|
---> viewControllers for each tab
... and it's root or vc1 that wants to dismiss. If I'm right about that, then, given the docs, the solution is clear:
[self.navigationController dismissViewControllerAnimated:YES completion:^{}];
will put us back on the tabbar vc on whatever tab was visible when we did the present.
I have a tab view controller with a navigation controller. In the first tab item I click on a button in a view that pops up a view with animated: YES.
Then when that view is done I hit another button that dismisses it. Like:
[self dismissViewControllerAnimated:NO completion:^{
ProfilesViewController *profile = [[ProfilesViewController alloc] init];
[self.navigationController pushViewController:profile animated:YES];
//SHOW YOUR NEW VIEW CONTROLLER HERE!
}];
But everytime this code runs, it dismisses the view, DOES NOT push the profiles controller, and shows the view from the first tab bar item.
How do I push the ProfilesViewController to the screen with a Back arrow?
If you are using dismissViewControllerAnimated to dismiss that means that the VC is presented modally. As such, it doesn't have a navigation controller (so self.navigationController is nil) and thus it can't push anything into the navigation controller.
You should really add a property to the controller which is a delegate or a completion block which can be used to push the controller from another controller (the one that presents it) to dismiss and push the controller.
A second option is to pass the navigation controller, it's a similar amount of code to using a block but not so good.
A crappy option is to use the parentViewController to find the appropriate navigation controller, but that sucks for many reasons.
Here is my problem. I had a UITableViewcontroller to which UINavigation controller is Embedded. To UITableviewController I had Add screen and an edit screen. Add screen is working perfectly. When I click on the records on the table view cell it is able to redirect to the edit page (detailed view). When I hit on Submit button the page is not navigating to the tableview cell. Here is the below error.
Warning: Attempt to present on whose view is not in the window hierarchy!
Here is the code I was trying for navigation.
UINavigationController *questionnaireNavController = [self.storyboard instantiateViewControllerWithIdentifier:#"DLProjectsTasksubtasks"];
[questionnaireNavController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
DLProjectsTasksubtasks *qvc = (DLProjectsTasksubtasks *)[questionnaireNavController topViewController];
[qvc.tableView reloadData];
[self presentViewController:questionnaireNavController animated:YES completion:nil];
[self dismissViewControllerAnimated:YES completion:nil];
Between I was using a Segue for transfering the data from tableview data to Edit Screen
I think your segue is being pushed on the navigation controller rather than being presented modally. So you have two options, either change the segue in your storyboard to be modal and use the dismissViewControllerAnimated:YES completion: or keep it the way it is and use popViewControllerAnimated: on your navigation controller.
[self.navigationController popViewControllerAnimated:YES];
For passing the data to the previous page you can either pass a reference of the previous view controller OR accessing it through your navigationController.viewControllers. Index n-2 will be your previous view controller (n is the count of viewControllers).
I want to know what is the different of them.
when I can call
[self dismissViewControllerAnimated:YES completion:nil];
and when I should call
[self.navigationController popViewControllerAnimated:YES];
according document of apple:
dismissViewControllerAnimated means
"Dismisses the view controller that was presented by the receiver."
But I always fail to dismiss view controller by this method.
-dismissViewControllerAnimated:completion:
Used to dismiss an UIViewController, which was presented by the method:
-presentViewController:animated:completion:.
-popViewControllerAnimated:
Method of UINavigationController is used to pop a controller shown by
-pushViewController:animated method of UINavigationController.
In the first case the view controller's view shows as a modal controller (usually from bottom to top), and the second case you are pushing a view controller in the navigation stack of UINavigationController.
your selected application is navigation based application means
[self.navigationController popViewControllerAnimated:YES];
your selected application is other than the navigation based application means
[self dismissViewControllerAnimated:YES completion:nil];