更改导航后退按钮的目的地

Change destination of Navigation Back button

如何更改默认导航后退按钮将我带到的视图控制器?后退按钮通常会将您带回到上一个视图控制器。但是如果我想让它通过两个视图控制器返回怎么办?我的意思是我想更改后退按钮将我带到的视图控制器。我不喜欢创建自定义后退按钮。那么有没有别的办法呢?可能是链接到后退按钮或其他东西的展开转场?

您可以像这样添加自定义后退按钮来完成此操作。点击导航栏上的后退按钮添加您的自定义操作处理程序。

+ (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;
    }
}

您可以覆盖 willMoveToParentViewController。在将视图控制器添加到容器视图控制器(如 UINavigationController)或从容器视图控制器中删除之前调用此方法。添加时,parent参数包含容器视图控制器,删除时,parent参数为nil。

此时您可以删除(没有动画)导航堆栈中的倒数第二个视图控制器,如下所示:

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)
            }
        }
    }
}

您也可以删除多个或添加新的。只要确保当你完成后你的数组包含至少 2 个视图控制器,最后一个不变(最后一个是被删除的,它会在调用此方法后自动从数组中删除)。

另请注意,可以使用 nil 参数多次调用此方法。例如,如果您尝试使用边缘滑动弹出视图控制器但在中间中止,则每次尝试时都会调用该方法。请务必添加额外的检查,以确保您不会删除比您想要的更多的视图控制器。

实现您想要的行为的最简单方法可能是使用 navigationController.setViewControllers(controllers, animated: true).

如果你想从控制器 A 返回 2 个控制器而不是一个,请在呈现 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)
        }
    }
}

这是一个很好的教程,如何在 Interface Builder 中为您的 segue 设置自定义 class:http://blog.jimjh.com/a-short-tutorial-on-custom-storyboard-segues.html

如果您通过代码呈现控制器,请使用此类别:

extension UINavigationController {
    func replaceCurrentViewControllerWith(viewController: UIViewController, animated: Bool) {
        var controllers = viewControllers
        controllers.removeLast()
        controllers.append(viewController)
        setViewControllers(controllers, animated: animated)
    }
}

然后只需使用 self.navigationController!.replaceCurrentViewControllerWith(newController, animated: true) 而不是 self.navigationControllerPushViewController(newController, animated: true)

很容易调整此代码以根据需要删除尽可能多的控制器。

这可能只是重复上面说的,但是我通过修改最后一个VewController中的viewDidLoad解决了这个问题。 "theControllers" 是一个 UIViewController 类型的数组。在我的例子中,我只是想回到根视图控制器,所以数组只包含根 viewcontroller(第一个)和当前 viewcontroller(最后一个)。然后 "setViewControllers" 将当前堆栈替换为我创建的堆栈。

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)
}