This question already has answers here:
How do I pop two views at once from a navigation controller?
(18 answers)
Closed 9 years ago.
Is it possible to pop the navigation controller twice? I have this navigation structure:
View One ----> View Two -----> View Three
What I'd like to accomplish is that by the tap of a row on View Three, go back directly to View One. I've done it from Three to Two via protocol-delegate, but setting the delegate in view One doesn't work and setting two consecutive delegate-protocol both poping the navigation controller, gives me error: nested navigation controller activity (or something similar).
Any help would be appreciated. Thanks in advance!
There is a few pop options
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
The first pops the top controller.
The second allows you to pop the whole stack off to get to the root.
The third allows you to pop to any viewController you have a reference to. You can get the viewController with self.navigationController.viewControllers and then work with the array to get the specific viewController you want to pop to
Use the following code. You can use any number instead of -3 to pop to a different level.
Obj-C:
ViewController *View = [self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count-3];
[self.navigationController popToViewController:View animated:YES];
Swift 3.0*:
let controller = self.navigationController?.viewControllers[(self.navigationController?.viewControllers.count)! - 3]
self.navigationController?.popToViewController(controller!, animated: true)
You can try this
[self.navigationController popToViewController:[[self.navigationController viewControllers] objectAtIndex:1] animated:YES];
Hope it Helps!!
'self' seems to be released after first pop
UINavigationController *navigationController = self.navigationController;
[navigationController popViewControllerAnimated:NO];
[navigationController popViewControllerAnimated:YES];
Related
I am new to coding objective-C and am trying to use pop to viewController. To do this when a button is tapped i use
[self.navigationController popToViewController:(what goes here) animated:YES];
I know its a UIViewController* that goes there but I am wondering where do I declare this and what code would I use to declare it.
My basic storyboard is I have 4 view controller A,B,C,D and A is the root which pushed to B which pushes to C which pushes to D, my button is on D and I am trying to pop back to B. The object at index method won't work because its doesn't always go A->B->C->D sometimes it goes A->C->B->D so
[self.navigationController popToViewController: [self.navigationController.viewControllers objectAtIndex:2] animated:YES];
So that doesn't work.
Thanks for the help in advance. Sorry if this question is too basic.
You need a way to find the desired view controller to pop to.
-(IBAction)popToDesiredViewController:(id)sender
{
UIViewController *desiredVC = nil;
// LOOK AT ALL VIEW CONTROLLERS IN NAVIGATION CONTROLLER
for (UIViewController *oneVC in self.navigationController.viewControllers) {
// CHECK IF THIS IS THE VIEW CONTROLLER YOU WANT
// change this to your logic
BOOL foundDesiredVC = [oneVC isKindOfClass: [SignInVC class]];
if (foundDesiredVC) {
desiredVC = oneVC;
break;
}
}
[self.navigationController popToViewController:desiredVC animated:YES];
}
Personally I don't find this as a basic question, even I got struck in such a situation. But I found a good solution by using “Mediator Design pattern” and develop my custom UINavigationController as a coordinator by combining the method:
- (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated
and maintaining my own navigation stack.
A coordinator (your custom UINavigationController) has to have the control over your navigation, if your are having unpredictable way of navigation.
I have a View that has a button in it, something like this Button---(segue)---> TableViewControllerNO1-----(segue)-->TableViewControllerNO2. In the TableViewControllerNO2 I delete all the rows by pressing a button and I want after I delete the rows to return to the first View, the one before the TableViewControllerNO1, how can I dismiss the two TableViewControllers from TableViewControllerNO2?
You have a cool method to do this: popToViewController
[self.navigationController popToViewController:viewController animated:YES];
So the important is inject a reference to that viewController.
Otherwise you can also do:
[self.navigationController popViewControllerAnimated:NO];
[self.navigationController popViewControllerAnimated:YES];
but the right way is the first.
You should use - (NSArray *)popToRootViewControllerAnimated:(BOOL)animated (assuming you are using a navigation controller). This would take you all the way back to the root view controller in your navigation stack, and use the standard animation.
In android, switching between activities, is fairly straightforward
you call
Intent intent = new Intent(this,NextActivity.class); <- define the next activity
startActivity(intent); <- start the next activity
finish(); < -get rid of the current activity
now in iOS i know how to do this:
UIViewController *nextviewcontroller = [[UIViewController alloc]initWithNibName:#"nextvc" bundle:nil];
[self presentViewcontroller:nextviewcontroller animated:YES completion:nil];
How do I get rid of the current view controller? so that currentviewcontroller dies after presenting nextviewcontroller ?
[self dismissViewController:YES]; doesnt seem to do the trick
the lifecycle methods viewWillDisappear and viewDidDisappear are called even if I don't call [self dismissViewController:YES];
i want "currentviewcontroller" to be removed from the memory, and from the viewcontroller stack, so that clicking "back" in "nextviewcontroller" will go to some thirdviewcontroller that was before currentviewcontroller
In iOS is different, since there's no concept of Activity and everything is more focused on the app itself (in Android you can mix activities from different apps). Therefore, there's no concept of "view controller stack".
The most similar concept is the "navigation stack" of navigation controllers, where you actually push and pop new view controller into some kind of linear navigation. A navigation bar is automatically created and populated with back buttons.
presentViewController will show your view controller modally upon the current one, but you can't thrash the presenting one since it's holding and containing ("defining context") the new one.
If you use a navigation controller for your navigation hierarchy (I don't know if you can), you can override the back button and use something like
UIViewController * prev = self.navigationController.viewControllers[self.navigationController.viewControllers.count -2 ]
[self.navigationController popToViewController:prev animated:YES]
With a modal view controller, you may try something like (I haven't tried but it may work)
[self.presentingViewController.navigationController popViewControllerAnimated:YES]
You should write one of these code into the target action of your close button.
iOS doesn't maintain a global stack of controllers in the way that Android does. Each app shows a controller at its root, and that one is responsible for showing the other controllers in the app. Controllers can display other controllers modally using presentViewcontroller:animated:completion: but the presenting controller remains underneath the presented one.
If your current controller is the root controller, then instead of using presentViewcontroller:animated:completion: you'd just do this:
self.view.window.rootViewController = nextViewController;
It's very common for the root controller to be a UINavigationController, which does manage a stack of controllers. If that is the case, and if your current controller is at the top of the stack, you'd do this:
[self.navigationController popViewControllerAnimated:NO];
[self.navigationController pushViewController:nextViewController animated:YES];
If your setup is different, you'd do something different; it's hard to say what without knowing more. But it's most likely that you'd be in the UINavigationController case.
In the viewDidAppear of your nextviewcontroller you could add :
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
NSArray *controllers = self.navigationController.viewControllers;
NSMutableArray *newViewControllers = [NSMutableArray arrayWithArray:controllers];
[newViewControllers removeObjectAtIndex:[controllers count]-2];
self.navigationController.viewControllers = newViewControllers;
}
There is nothing available like this in iOS but you can achieve it doing something like below
NSArray *viewControllers=[self.navigationController viewControllers];
NSMutableArray *newControllers=[[NSMutableArray alloc] init];
for(int i=[viewControllers indexOfObject:self];i<viewControllers.count;i++){
[newControllers addObject:[viewControllers objectAtIndex:i]];
}
[self.navigationController setViewControllers:[[NSArray alloc] initWithArray:newControllers]];
I have tried the method of storing all the view controllers in an array but it didn't work for me . When you try popViewController it will move to the View Controller which is last in the stack.
You can make 2 navigation controllers and switch between them and also switch between the view controllers of a particular Navigation Controller.
For eg.
You can switch between 2 Navigation Controller using the following code:
FirstNavController *fisrtView=[storyboard instantiateViewControllerWithIdentifier:#"firstnavcontroller"];
self.window.rootViewController = firstView;
}else{
SecondNavController *secondView=[storyboard instantiateViewControllerWithIdentifier:#"loginnavcontroller"];
self.window.rootViewController = secondView;
}
If your FirstNavController has 2 ViewControllers then you can switch between them using pushViewController
SecondViewController *sc = [self.storyboard instantiateViewControllerWithIdentifier:#"secondviewcontroller"];
[[self navigationController] pushViewController:sc animated:YES];
and popViewController
[self.navigationController popViewControllerAnimated:YES];
In my app, I want to remove a view controller from the stack of view controllers and then navigate to a particular view controller. Say I have A,B,C,D,E view controller stack. I wish to remove E and then go to C. I use the following code
NSMutableArray *allViewControllers = [NSMutableArray arrayWithArray:[self.navigationController viewControllers]];
for (UIViewController *aViewController in allViewControllers)
{
if ([aViewController isKindOfClass:[noNetworkViewController class]])
{
[aViewController removeFromParentViewController];
}
}
NSMutableArray *allbViewControllers = [NSMutableArray arrayWithArray:[self.navigationController viewControllers]];
for (UIViewController *aViewController in allbViewControllers)
{
if ([aViewController isKindOfClass:[messageViewController class]])
{
[self.navigationController popToViewController:aViewController animated:NO];
}
}
The first for loop erases all the view controllers from the stack. Is there something I am doing wrong here. Please let me know.
You should not be mucking around with a navigation controller's stack of view controllers using removeFromParentViewController:. Ever. That is flat-out wrong.
Furthermore, there is no reason to loop through a navigation controller's stack of view controllers and popping them one by one. There are methods like popToViewController:animated: and popToRootViewController that let you pop to any arbitrary view controller in the stack with one call.
Have you verified that the parent view controller property is populated by the navigation controller? The parent view controller is normally used for UIViewControllerContainment protocol conformance. UINavigationController does not conform to that protocol, which makes me think you're not going to be able to remove it from the navigation controller by calling that method. You can use – setViewControllers:animated: on your navigationController to assign the controllers. Just use the array you created with all the viewControllers.
If you are using a pop, you don't have to remove the other view from it's parent view. You can just pop to a view controller that is in the view controller array and back two from the top of the stack.
I would give a code example, but i'm not on my dev computer. If nobody has answered with more detail before tomorrow, I will add some code.
-Edit - Here is the line of code that I use to pop back 2 view controllers. I'm sure this could be done better ways, but it works.
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:[self.navigationController.viewControllers count]-3] animated:YES];
I need to jump between view controllers. For example:
View1: First screen (Just logo)
View2: Download Screen
View3: First app screen (Some Buttons)
View4-View(N): some app screens
When user enters app the app goes to View1->View2 (downloads stuff)->View 3->View4->View5
Then user wish to go to First app screen (View3) he does:
NSArray *array = [self.navigationController viewControllers];
[self.navigationController popToViewController:[array objectAtIndex:2] animated:NO];
The first time user enters the app it goes: View1->View3 (The download screen no longer needed), and I have different push segue to go to View3 so lets assume the user goes to: View1->View 3->View4->View5, now he wishes to go back to View3, so the function:
NSArray *array = [self.navigationController viewControllers];
[self.navigationController popToViewController:[array objectAtIndex:2] animated:NO];
Will return him to View4, and this is WRONG. How can I solve it?
Well if you are using storyboards , what you can do is set your uiviewcontrollers' storyboard id and use it for popping and pushing your views.
Lets say your Storyboards name is MainStoryboard.storyboard
UIStoryboard* sb = [UIStoryboard storyboardWithName:#"MainStoryboard"
bundle:nil];
SettingsListViewController *settingsVC = [sb instantiateViewControllerWithIdentifier:#"SettingsListViewController"]; // #"SettingsListViewController" is the string you have set in above picture
[self.navigationController popToViewController:settingsVC animated:YES];
Above solution should work for you , but If I were you I would change the structure of my app , you say :
View1: First screen (Just logo)
View2: Download Screen
Since View1 is just logo and View 2 is also a view that you skip, you can remove them from navigation controller and make View3 your navigation controller's root view controller and when you need view1 and view2 present them as Modal View Controllers,
when you are done with them lets say; user successfully loaded the app dismiss logo screen and present Download Screen if download successful then dismiss it.
So your View3 Will be there as root view controller, lets say your at View(n) you want to go to home screen which is View3 all you need to do is call
[self.navigationController popToRootViewControllerAnimated:NO];
When you are on view(n) and want to pop to view(n-1) just use
[self.navigationController popViewControllerAnimated:YES];
good luck,
I always use this, and it will work fine in your case. In fact the following line of code is copied to my "Notes" for quick copy/paste.
Be sure to import your ViewController.h file, if not.
for (UIViewController *viewController in self.navigationController.viewControllers) {
if([viewController isKindOfClass:["your ViewController" class]]) {
[self.navigationController popToViewController:viewController animated:NO];
}
}
In the second sequence your navigation stack changes and view3 would be at index 1. so
[[self.navigationController popToViewController:[array objectAtIndex:1] animated:NO];
would be the right way of doing it.
according to your sitation use directly name of viewController
create instance of your viewController,like this
i supposed here that your viewController name is-view3Controller
View3Controller view3Controller=[[View3Controller alloc]init];
[self.navigationController popToViewController:#"view3Controller" animated:NO]
or if you are using storyboard then
View3Controller view3Controller=[self.storyboard instantiateViewControllerWithIdentifier:#"view3Controller"];
[self.navigationController popToViewController:#"view3Controller" animated:NO]
It sounds like View 2 is not being added to your view controllers array at run time, possibly because of the segue you've created.
Try removing the segue that transitions from View 1 > View 3 and pushing the user past View 2 without animating, as your application's logic requires it:
// If the user needs to skip ahead to view 3, conditionally push view 2 and view 3 without animating
[self.navigationController pushViewController:viewController2 animated:NO];
[self.navigationController pushViewController:viewController3 animated:NO];
Alternatively if you left the segue in place could you not look at the size of the UINavigationController viewControllers property and "guess" based on the size whether or not you skipped View 2? If you did then you can adjust the popToViewController method to pop to the correct index. This is, admittedly less elegant and brittle if you need to skip other views as well.
// Check length of viewController array with 'N' views (pseudo code)
if (self.navigationController.viewControllers.length == N-1)
// View 2 was ignored: pop to objectAtIndex:1
else
// View 2 was included: pop to objectAtIndex:2
If I understood you correctly, your view3 has special view controller, so you can use such code:
NSArray *VCs = [self.navigationController viewControllers];
for (UIViewController *VC in VCs)
{
if ([VC isKindOfClass:[**YOUR-VIEW-CONTROLLER** class]]) {
[self.navigationController popToViewController:VC animated:NO];
}
}
It's simple and it works!
for (UIViewController* controller in self.navigationController.viewControllers) {
if ([controller isKindOfClass:[<Your View Controller Name> class]]) {
[self.navigationController popToViewController:controller animated:YES];
return;
}
}
Go like this to a particular view controller
[self.navigationController popToViewController:[[self.navigationController viewControllers]objectAtIndex:1] animated:YES];