How do I pop the view controller underneath a pushed view controller? - iphone

I want to push a view controller onto the stack, then pop the first one that pushed the new one.
-(void) someMethod {
MegaSuperAwesomeViewController *tempVC = [[MegaSuperAwesomeViewController alloc] init];
[self.navigationController pushViewController:tempVC animated:YES];
[tempVC release];
// pop this VC, how?
}
EDIT: turns out I can pop back 2 view controllers instead once finished with the new VC. Still not what I wanted exactly, but it works. The downside is I need to set a flag to indicate that the covered view is completed.

Here's a technique of popping back two view controllers, which has a similar problem of yours of the current view controller and its navigationController property going away as soon as you do the first pop:
// pop back 2 controllers on the stack to the setup screen
//
// locally store the navigation controller since
// self.navigationController will be nil once we are popped
//
UINavigationController *navController = self.navigationController;
// retain ourselves so that the controller will still exist once it's popped off
//
[[self retain] autorelease];
// Pop back 2 controllers to the setup screen
//
[navController popViewControllerAnimated:NO];
[navController popViewControllerAnimated:YES];
alternatively, you can directly "party" on the navigation controllers stack of view controllers:
setViewControllers:animated: Replaces
the view controllers currently managed
by the navigation controller with the
specified items.
(void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated Parameters
viewControllers The view controllers
to place in the stack. The
front-to-back order of the controllers
in this array represents the new
bottom-to-top order of the controllers
in the navigation stack. Thus, the
last item added to the array becomes
the top item of the navigation stack.
animated If YES, animate the pushing
or popping of the top view controller.
If NO, replace the view controllers
without any animations. Discussion You
can use this method to update or
replace the current view controller
stack without pushing or popping each
controller explicitly. In addition,
this method lets you update the set of
controllers without animating the
changes, which might be appropriate at
launch time when you want to return
the navigation controller to a
previous state.
If animations are enabled, this method
decides which type of transition to
perform based on whether the last item
in the items array is already in the
navigation stack. If the view
controller is currently in the stack,
but is not the topmost item, this
method uses a pop transition; if it is
the topmost item, no transition is
performed. If the view controller is
not on the stack, this method uses a
push transition. Only one transition
is performed, but when that transition
finishes, the entire contents of the
stack are replaced with the new view
controllers. For example, if
controllers A, B, and C are on the
stack and you set controllers D, A,
and B, this method uses a pop
transition and the resulting stack
contains the controllers D, A, and B.
Availability Available in iOS 3.0 and
later. Declared In
UINavigationController.h
So, to "disappear" the view controller directly under you on the navigation stack, in your view controller's viewDidLoad, you could do this:
NSMutableArray *VCs = [self.navigationController.viewControllers mutableCopy];
[VCs removeObjectAtIndex:[VCs count] - 2];
self.navigationController.viewControllers = VCs;

I had trouble figuring this out also so I wanted to share how I got this to work.
Let's say you have a stack of VCs VC1 being the root then you push VC2 and from VC2 you want to push VC3 but once pushed you don't want the user to go back to VC2 but rather to VC1 (the root). The way to do that is:
//push VC3 from VC2
[[self navigationController] pushViewController:VC3 animated:YES];
// now remove VC2 from the view controllers array so we will jump straight back to VC1
NSMutableArray *viewHeirarchy =[[NSMutableArray alloc] initWithArray:[self.navigationController viewControllers]];
[viewHeirarchy removeObject:self];
self.navigationController.viewControllers = viewHeirarchy;
Hope this helps someone else

Thanks Bogatyr about the tip on 'party on the viewcontroller array for the navcontroller'. I just replaced the entire stack with the one viewcontroller I want to change to, and then log out all the viewcontrollers in the stack to make sure its the only one! Worked great - thanks!
RatingsTableViewController *newViewController = [[RatingsTableViewController alloc] init];
NSMutableArray * newVCarray = [NSMutableArray arrayWithObjects:newViewController, nil];
self.navigationController.viewControllers = newVCarray;
[newViewController release];
NSMutableArray *allControllers = [[NSMutableArray alloc] initWithArray:self.navigationController.viewControllers];
for (id object in allControllers) {
NSLog(#"name VC: %#", object);
}
[allControllers release];

-(void)popToSelf{
NSArray *array = [self.navigationController viewControllers];
for (int i = 0 ; i < array.count ; i++) {
UIViewController *currentVC = [array objectAtIndex:i];
if ([currentVC isKindOfClass:[YourViewControllerClass class]]) {
[self.navigationController popToViewController:[array objectAtIndex:i] animated:YES];
}
}
}

Related

popToViewController with a specific UITabBarController Tab/View

I am using popToViewController method to pop to a view controller. What I was trying to achieve is to pop to an UITabBarController's specific index.
NSArray *viewArrays = [self.navigationController viewControllers];
So my view hierarchy is;
<LoginViewController: 0x6b75850>,
<UITabBarController: 0x6ba0b50>,
<RequestViewController: 0x684fe90>,
<ApplyViewController: 0x6845790>
Following code does pops to my UITabBarController, but since I was on the third view of my UITabBarController, it pops back to the third view
[[self navigationController] popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];
What I want is to pop to the second view of my UITabBarController.
Edit (Answer)
As stated, we don't push or pop. So in order to gather your tabBarController you either use;
UITabBarController *myTabController = [self.navigationController.viewControllers objectAtIndex:indexOfYourTabBarControllerInYourNavigationController];
myTabController.selectedIndex = indexToGo;
or if you are on one of your tabBarController views;
self.tabBarController.selectedIndex = indexToGo;
In UITabBarController you dont pop and push, if you want to go to the second view you would set the selected index instead
Like so
yourTabController.selectedIndex = 1;
Or if the current view controller is a part of the tabBarController do
self.tabBarController.selectedIndex = 1;

Removing viewcontrollers from navigation stack in the right way

I have 5 view controllers(say A,B,C,D,E) in my navigation stack. ViewController E is in the top of the stack. On a button click in ViewController E, I want to move to ViewController C. For that I am using the following code.
NSMutableArray *navigationarray = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
[navigationarray removeObjectAtIndex:4];
[navigationarray removeObjectAtIndex:3];
self.navigationController.viewControllers = navigationarray;
[navigationarray release];
Is there a better way to do this, where I can check which viewController is being removed from the navigation array
Edit: In this case, could I check whether the viewcontroller being removed isKindOfClass of the class of the particular view controller like
if ([[navigationarray objectAtIndex:4] isKindOfClass:[MyClass class]])
Popping view controller from navigation controller is the easy way. This will remove VC E and D.
[self.navigationController popToViewController:viewControllerC animated:YES];

Switching from View Controller in stack to View Controller not on stack

I'm hoping you can help me out. I've designed an app that is a Tab Bar app. In the view controller for the first tab, there is a button, that when pressed, generates a modal view. I've initialized a nav controller on that modal view, because when I hit the "Save" button on my modal view (which I use to input user data), I push another table view (which shows a table of all user inputted data so far). On that stacked table view, I have a "Done" button, which when pressed, should go to another view on the tab (a progress view of user input), that is, OFF the stack.
So my question is, if I'm two controllers into the stack, how to do I pop off the stack to another view NOT on the stack? I've used the popToViewController method, but as you may have guessed, I get the "Tried to pop to a view controller that doesn't exist" message. Here's my simple code in the second view on the stack:
- (IBAction)doneButtonPressed:(id)sender
{
LogTableViewController *logTableViewController = [[LogTableViewController alloc]init];
[self.navigationController popToViewController:logTableViewController animated:YES];
[logTableViewController release];
}
Where LogTableViewController is not on the stack, but is rather just another target for another tab in the app. Any ideas? Thanks in advance.
I'm not entirely sure why you just can't push the new view controller to the stack, but if you need to pop to it, you can do:
//create new VC
LogTableViewController *newVC = [[LogTableViewController alloc]init];;
//get VC stack
NSMutableArray * newControllers = [NSMutableArray arrayWithArray: self.navigationController.viewControllers];
//choose where to insert the new vc
NSUInteger insert_index = [newControllers count] - 1;
//insert into the stack
[newControllers insertObject:newVC atIndex:insert_index];
//replace stacks
[self.navigationController setViewControllers: newControllers];
//pop to your new controller
[self.navigationController popViewControllerAnimated:YES];
hope this helps.
You were close. Just do:
- (IBAction)doneButtonPressed:(id)sender
{
LogTableViewController *logTableViewController = [[LogTableViewController alloc]init];
[self.navigationController setViewControllers:[NSArray arrayWithObject:logTableViewController] animated:YES];
[logTableViewController release];
}

popToViewController, then pushViewController

After my rootViewController, there is a workflow that would go like this
viewController1 -> push viewController2 -> push viewController3 -> ***** viewController2
I would like to popToViewController3. However, I need to go to viewController1 first because viewController1 basically determines which instance of viewController2 to push. So I
// pseudo code
[popToViewController:vc1 animated:NO];
[self.navigationController pushViewController2 animated:YES];
I basically pop before pushing so I don't end up getting vc2, vc3, vc2, vc3, vc2 etc. But in doing this, since it animates the push of the last viewController, it gives the user the impression that a new viewController was pushed onto the stack, vs popping to the previous viewController. Is there a way to get around this ? If I animate the pop and not the last push, I do not get the correct viewController I want on top by the looks of it. Thanks.
can you use something like
[self.navigationController setViewControllers:[NSArray arrayWithObject:yourViewController]
animated:YES];
you can fill the array with the view controllers you want on the stack.
#interface UINavigationController (PushThenPop)
-(void)popToViewController:(UIViewController*)rootViewController
thenPushViewController:(UIViewController*)pushViewController;
#end
#implementation UINavigationController (PushThenPop)
-(void)popToViewController:(UIViewController*)rootViewController
thenPushViewController:(UIViewController*)pushViewController
{
NSMutableArray *viewControllers = self.viewControllers.mutableCopy;
// Pop viewcontrollers until we reach the rootViewController
while(viewControllers.count >= 1) {
id last = [viewControllers lastObject];
if (last == rootViewController) break;
[viewControllers removeLastObject];
}
// Bail out the array is empty. We expect the array to contain at least 1 element, the rootViewController.
if (!viewControllers.count) {
return;
}
// Push the new view controller
[viewControllers addObject:pushViewController];
self.viewControllers = viewControllers.copy;
}
#end
You can retrieve (viewControllers), modify, and "reinstall" (setViewControllers) the list of view controllers managed by the navigation controller, and the "reinstall" can (in theory) be animated. (However, I had some problems with animation in practice, though I don't recall the details.)

How can I go back to the first view from the third view directly?

When I touch cancel button in the third view, I want to go back to the first view directly. How can I do that?
This is the code.
// this part is in the first view.
SecondController *aSecondController = [[SecondController alloc] init];
UINavigationController *aNaviController = [[UINavigationController alloc] initWithRootViewController:aSecondController];
self.naviController = aNaviController;
[aNaviController release];
[aSecondController release];
[self.view addSubview:naviController.view];
// this part is in the second view.
ThirdController *thirdController = [[ThirdController alloc] initWithStyle:UITableViewStyleGrouped];
[self.navigationController pushViewController:thirdView];
[thirdView release];
// this part is in the third view.
- (void)cancel {
[self.navigationController popViewControllerAnimated:NO]; // this only goes to the second view.
}
popToViewController, popToRootViewController only go to the second view also.
You can use popToRootViewController:animated: method, if your root view controller is the one you're after. You can also use popToViewController:animated: to specify which controller you want to end up with on the top of the navigation stack.
Use UINavigationControllers -popToRootViewControllerAnimated:
- (void)cancel {
[self.navigationController popToRootViewControllerAnimated:NO];
}
And if you ever want to pop to a specific view controller you can use popToViewController:animated: and use the viewControllers property to get the view controller at the correct index.
Set up the navigation controller in your app delegate. Make the first view controller the nav controller's root controller instead of having the first view controller own the nav controller. Then you can use -popToRootViewController:animated: as the other answers have suggested.