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?
问题:
回答1:
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.
回答2:
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.
回答3:
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)
}
回答4:
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;
}
}