10

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.

TigerCoding
  • 8,710
  • 10
  • 47
  • 72

5 Answers5

16

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;
vance
  • 3
  • 2
Bogatyr
  • 19,255
  • 7
  • 59
  • 72
  • Thanks for the attempt but the nav array is incompatible with the VCs array you describe, because it has to be mutable. however, it's not really needed because I can pop back two VC from the new one when finished. Don't know why I didn't spot it. – TigerCoding Feb 13 '11 at 18:53
  • Just added in a "Mutable" in an edit -- small misnake, the technique works though. – Bogatyr Feb 13 '11 at 18:59
  • it's not compatible, so I guess I'd have to make a new array, remove the vc, then place the array back? I wonder if that would cause problems... – TigerCoding Feb 13 '11 at 19:27
  • OK. I didn't vet the code in a real build, but you've got the idea: grab the viewControllers array, edit it, and set it back. No problems, the api specifically allows you to set a new array of view controllers, see "setViewControllers" in my answer. – Bogatyr Feb 13 '11 at 19:30
8

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

timosdk
  • 151
  • 2
  • 8
  • 1
    Other methods left the old view controller's navigation items in the stack. This is the only one that works properly for me now that iOS 7 introduces popGestures. Thanks! – chourobin Feb 17 '14 at 21:06
  • Excellent, this worked for me too. Initially I was trying to do this after VC2 had pushed VC3 (in UINavigationController's delegate method) which works in iOS8, but in iOS7 doing it directly after pushing is needed to make the navigation items correctly pop. It also means simpler code! – Rajveer Oct 21 '14 at 11:24
0
-(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];
        }
    }
}
TtheTank
  • 332
  • 3
  • 8
0

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];
hoak
  • 125
  • 1
  • 7
0

I've done this recently in Swift using the below code.

private func removePushedLoadingViewFromNavHeirarchy() {
    let viewHeirarchyWithoutLoadingView = rootNavigationController
        .viewControllers
        .filter { $0 as? FullScreenLoadingViewController == nil }
    rootNavigationController.viewControllers = viewHeirarchyWithoutLoadingView
}

I call this after pushing another view controller like so

let viewController = ReviewViewController()
rootNavigationController.pushViewController(viewController, animated: true)
removePushedLoadingViewFromNavHeirarchy()
Harry Bloom
  • 2,359
  • 25
  • 17