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];
Related
i have an app with 5 views, mostly consisting of drill downs.
lets say i drill down to the 4th view controller. is there a way to present the second view controller exactly as it is without recreating it and modally present that view?
the drill downs do a sort of round about and i dont want to force the user to reselect their selection on the first view to bring them into the second view
so its like this (tvc = tableviewcontroller)
tvc1 > tvc2 > tvc3 > tvc4 > tvc2 > tvc5
^ ^
these two views are the same view in memory
You cannot present tvc2 again while it is already in the "stack" of presented view controllers.
If you push your view controllers onto a navigation controller's stack, then you can change the order of the view controllers in that stack by assigning to its viewControllers property or by sending it setViewControllers:animated:. You can hide its navigation bar if you don't want users to see it. I don't think it's safe to put the same view controller into the stack in two places at the same time.
You can possibly iterate over the visible viewControllers, and use the is casting operator to check if it is that type of class. Then find the view controller and pop to it.
Objective-C
for (id viewController in self.navigationController.viewControllers) {
if ([viewController isKindOfClass:[ViewControllerClass class]]) {
[self.navigationController popToViewController:viewController animated:TRUE];
}
}
Swift 2.0
for viewController in self.navigationController!.viewControllers {
if viewController is ViewControllerClass {
self.navigationController?.popToViewController(viewController, animated: true)
}
}
UINavigationController has an array called viewControllers. This will give you the list of UIViewControllers that exist in that navigation stack.
You can try something like:
UIViewController *yourTableViewController = [self.navigationController.viewControllers objectAtIndex:(theIndexOfYourTableViewController)];
Hope this helps.
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.
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];
For some reason, this code returns 0 elements:
NSArray *viewControllers = [[self navigationController] viewControllers];
NSLog(#"numero: %d", viewControllers.count);
I am reaching my current ViewController (B), from viewController (A), by calling:
[self performSegueWithIdentifier:#"NumericKeyboard" sender:self];
What am I missing here?
Thank you!
If the segue to view controller B is not a push segue, view controller B will not be in the stack of view controllers owned by the navigation controller.
Therefore, [self navigationController] will be nil when called against view controller B.
If you need view controller B to be in the stack automatically, you must change the segue to a push segue.
Alternatively, you can manually add the controller to the stack.
I am using a NavigationViewController to navigate between master view and detailed views. Now I want to be able to switch to a sibling detail view without first showing the master view and I have tried doing popViewController and pushViewController in the same method or popViewController in the detailed view and then pushViewController in mater's viewDidLoad after popViewController but it won't work - the view just ends up going back to the master view without switching to the detail. Any idea what to do?
The solution suggested here doesn't work as far as I can tell:
Switching Content Views Issue
I've never tried it, but this should work:
// create instance of new view controller
MyViewController *myViewController = [[MyViewController alloc] init];
// get current stack of viewControllers from navigation controller
NSMutableArray *viewControllers = [[self.navigationController viewControllers] mutableCopy];
// replace top view controller in stack
[viewControllers replaceObjectAtIndex:([viewControllers count] - 1) withObject:myViewController];
// set modified stack of view controllers in navigation controller
[self.navigationController setViewControllers:viewControllers animated:YES];
According to the docs, your app will transition to the new view controller with a push animation, and then when back button is clicked it it will be as if the view controller you pushed from was never there. (If you don't want the animation, use animated:NO)