How can I change the view controller the default navigation back button takes me to? The back button usually takes you back to the previous view controller. But what if I want it to go back by two view controllers? I mean I want to change the view controller the back button takes me to. I do not prefer creating a custom back button. So is there any other way? Probably an unwind segue linked to the back button or something?
-
What do you mean by 'But what if I want it to go back by two view controllers'? – Caleb Kleveter Sep 15 '15 at 15:23
-
1@CalebKleveter I mean what if I want to go to another view controller and not the previous one? – Krish Wadhwana Sep 15 '15 at 15:24
4 Answers
Probably easiest way to achieve behaviour you want is to use navigationController.setViewControllers(controllers, animated: true)
.
If you want to go 2 controllers back from controller A instead of only one, use this custom segue when presenting A:
class ReplaceControllerSegue: UIStoryboardSegue {
override func perform() {
if let navigationController = sourceViewController.navigationController as UINavigationController? {
var controllers = navigationController.viewControllers
controllers.removeLast()
controllers.append(destinationViewController)
navigationController.setViewControllers(controllers, animated: true)
}
}
}
Here is nice tutorial how to set custom class for your segue in Interface Builder: http://blog.jimjh.com/a-short-tutorial-on-custom-storyboard-segues.html
If you are presenting controller via code, use this category:
extension UINavigationController {
func replaceCurrentViewControllerWith(viewController: UIViewController, animated: Bool) {
var controllers = viewControllers
controllers.removeLast()
controllers.append(viewController)
setViewControllers(controllers, animated: animated)
}
}
Then just use self.navigationController!.replaceCurrentViewControllerWith(newController, animated: true)
instead of self.navigationControllerPushViewController(newController, animated: true)
.
It's easy to adjust this code to remove as much controllers as you need.

- 3,367
- 18
- 14
-
Should I create a separate UIStoryboardSegue Cocoa Touch class for this? And if yes, how should I use this class? As in should I assign the class to some segue or what? – Krish Wadhwana Sep 15 '15 at 17:20
-
1I've posted code for custom UIStoryboardSegue class above. Here is tutorial how to assign custom class for segue in interface builder: http://blog.jimjh.com/a-short-tutorial-on-custom-storyboard-segues.html – glyuck Sep 15 '15 at 17:42
-
OH GOD! Thank you so much! I have searched SO for this but I didn't get it! Thanks a lot! – Krish Wadhwana Sep 15 '15 at 18:14
-
I don't get how the action of the back button is replaced. You only posted how to remove the last one. If you perform the segue it is clear, but if you do it in code? How do I know when to call your code? – testing Sep 18 '15 at 09:37
-
Use `self.navigationController?.replaceCurrentViewControllerWith(newController, animated: true)` (see extension code above) instead of `self.navigationController?.pushViewController(newController, animated: true)` – glyuck Sep 18 '15 at 13:10
You can override willMoveToParentViewController
. This method is called before your view controller is added in or removed from a container view controller (like a UINavigationController
). When it is being added, the parent
parameter contains the container view controller, when it is being removed, the parent
parameter is nil.
At this point you can remove (without animation) the second to last view controller in the navigation stack like so :
Objective-C
- (void)willMoveToParentViewController:(UIViewController *)parent
{
[super willMoveToParentViewController:parent];
if (parent == nil) {
NSArray *viewControllers = self.navigationController.viewControllers;
NSUInteger viewControllersCount = viewControllers.count;
if (viewControllersCount > 2) {
NSMutableArray *mutableViewControllers = [NSMutableArray arrayWithArray:viewControllers];
[mutableViewControllers removeObjectAtIndex:(viewControllersCount - 2)];
[self.navigationController setViewControllers:[NSArray arrayWithArray:mutableViewControllers] animated:NO];
}
}
}
Swift
override func willMoveToParentViewController(parent:UIViewController?)
{
super.willMoveToParentViewController(parent)
if (parent == nil) {
if let navigationController = self.navigationController {
var viewControllers = navigationController.viewControllers
var viewControllersCount = viewControllers.count
if (viewControllersCount > 2) {
viewControllers.removeAtIndex(viewControllersCount - 2)
navigationController.setViewControllers(viewControllers, animated:false)
}
}
}
}
You could also remove more than one or add new ones. Just make sure that when you are finished your array contains at least 2 view controllers with the last one unchanged (the last one is the one being removed and it will be removed from the array automatically after this method is called).
Also note than this method can be called more than once with a nil parameter. For example if you try to pop the view controller using the edge swipe but abort in the middle, the method will be called each time you try. Be sure to add additional checks to make sure that you don't remove more view controllers than you want.

- 5,409
- 2
- 17
- 47
-
1It was so difficult to find this answer. But you did it! Thanks! – Krish Wadhwana Sep 15 '15 at 18:16
-
Seems to work in the first place (I don't know all my edge cases yet). Works better than [`viewWillDisappear`](http://stackoverflow.com/a/3445994/426227). – testing Sep 18 '15 at 10:04
This may just repeat what has been said above, but I solved this by modifying viewDidLoad in the last VewController. "theControllers" is an array of type UIViewController. In my case, I just wanted to go back to the root view controller, so the array just contains the root viewcontroller (first) and the current viewcontroller (last). Then "setViewControllers" replaces the current stack with the one I created.
override func viewDidLoad() {
super.viewDidLoad()
var theControllers = [UIViewController]()
theControllers.append(self.navigationController!.viewControllers.first!)
theControllers.append(self.navigationController!.viewControllers.last!)
self.navigationController?.setViewControllers(theControllers, animated: false)
}

- 105
- 1
- 9
You can do this by adding a custom back button like this. Add your custom action handler on tap on Back button on navigation bar.
+ (void)addCustomBackButtonForController:(id <MyCustomBackButtonDelegate>)iViewController withNavBar:(UINavigationController *)iNavController andNavItem:(UINavigationItem *)iNavItem {
if ([self isIOS7orAbove]) {
UIImage *backArrow = [UIImage imageNamed:kMyIOS7BackButtonImage];
UIButton *aBackButton = [UIButton buttonWithType:UIButtonTypeSystem];
CGSize aBackButtonTextSize = [MyLocalizedBackButton sizeWithFont:[UIFont systemFontOfSize:17.0]];
aBackButton.frame = CGRectMake(0.0, 0.0, aBackButtonTextSize.width + backArrow.size.width, iNavController.navigationBar.frame.size.height);
aBackButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
SEL backSelector = NSSelectorFromString(@"backAction");
[aBackButton addTarget:iViewController action:backSelector forControlEvents:UIControlEventTouchUpInside];
[aBackButton setTitle:MyLocalizedBackButton forState:UIControlStateNormal];
[aBackButton setImage:backArrow forState:UIControlStateNormal];
[aBackButton setExclusiveTouch:YES];
if ([self isIOS7orAbove]) {
aBackButton.titleLabel.font = [UIFont systemFontOfSize:17.0];
} else {
aBackButton.titleLabel.font = [UIFont boldSystemFontOfSize:12.0];
}
UIBarButtonItem *aLeftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:aBackButton];
UIBarButtonItem *aNegativeSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
aNegativeSpacer.width = kMyIOS7BarButtonNegativeSpacerWidth;
iNavItem.leftBarButtonItems = @[aNegativeSpacer, aLeftBarButtonItem];
} else {
UIButton *aCustomBackButton = [UIButton buttonWithType:101];
SEL backSelector = NSSelectorFromString(@"backAction");
[aCustomBackButton addTarget:iViewController action:backSelector forControlEvents:UIControlEventTouchUpInside];
[aCustomBackButton setTitle:MyLocalizedBackButton forState:UIControlStateNormal];
[aCustomBackButton setExclusiveTouch:YES];
if ([self isIOS7orAbove]) {
aCustomBackButton.titleLabel.font = [UIFont systemFontOfSize:kFontSize17];
} else {
aCustomBackButton.titleLabel.font = [UIFont boldSystemFontOfSize:kFontSize12];
}
UIBarButtonItem *aLeftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:aCustomBackButton];
iNavItem.leftBarButtonItem = aLeftBarButtonItem;
}
}

- 37,684
- 43
- 191
- 309
-
I want to implement this without a custom back button or else I won't be able to swipe back and the back animation will be lost. A lot of apps have been able to implement this without a custom back button. Is there any way I can do this too? – Krish Wadhwana Sep 15 '15 at 15:50
-
What do you mean by won't be able to swipe back and the back animation will be lost? – Abhinav Sep 15 '15 at 15:51
-
You know the user can swipe from the edge and slowly transition to the previous view controller? – Krish Wadhwana Sep 15 '15 at 15:52
-
I see. I am not sure how would you achieve that. May be by adding your own swipe gesture recognizer to override default behaviour. Would be interesting to see this getting solutionize though :)! – Abhinav Sep 15 '15 at 15:55
-
Check out this app named "Swifty" on the AppStore. It has achieved this. Not to mention many other mediocre apps. – Krish Wadhwana Sep 15 '15 at 15:56
-
1I am sure there would be. Developers world is vast and with technology anything' possible :). Good luck with your application! – Abhinav Sep 15 '15 at 15:58