【问题标题】:Change destination of Navigation Back button更改导航返回按钮的目的地
【发布时间】:2015-12-11 22:19:48
【问题描述】:

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

【问题讨论】:

  • “但是如果我希望它通过两个视图控制器返回怎么办”是什么意思?
  • @CalebKleveter 我的意思是如果我想转到另一个视图控制器而不是前一个视图控制器怎么办?

标签: ios swift uiviewcontroller xcode6 uinavigationbar


【解决方案1】:

您可以通过添加这样的自定义后退按钮来做到这一点。点击导航栏上的后退按钮添加您的自定义操作处理程序。

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

【讨论】:

  • 我想在没有自定义后退按钮的情况下实现这一点,否则我将无法向后滑动并且后退动画将丢失。许多应用程序已经能够在没有自定义后退按钮的情况下实现这一点。有什么办法我也可以做到吗?
  • 不能往回滑动,后面的动画会丢失是什么意思?
  • 你知道用户可以从边缘滑动并慢慢过渡到上一个视图控制器吗?
  • 我明白了。我不确定您将如何实现这一目标。可能是通过添加您自己的滑动手势识别器来覆盖默认行为。看到这个解决方案会很有趣:)!
  • 我相信会有。开发人员的世界是广阔的,技术是一切皆有可能的:)。祝您申请顺利!
【解决方案2】:

您可以覆盖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];
        }
    }
}

斯威夫特

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 参数多次调用。例如,如果您尝试使用边缘滑动弹出视图控制器但在中间中止,则每次尝试时都会调用该方法。请务必添加额外的检查,以确保您移除的视图控制器数量不会超出您的预期。

【讨论】:

  • 很难找到这个答案。但是你做到了!谢谢!
  • 似乎一开始就可以工作(我还不知道我所有的边缘情况)。比viewWillDisappear 更好用。
【解决方案3】:

实现您想要的行为的最简单方法可能是使用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 设置自定义类: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)

您可以轻松调整此代码以删除所需数量的控制器。

【讨论】:

  • 我应该为此创建一个单独的 UIStoryboardSegue Cocoa Touch 类吗?如果是,我应该如何使用这个类?就像我应该将课程分配给某个 segue 还是什么?
  • 我已经发布了上面自定义 UIStoryboardSegue 类的代码。以下是如何在界面生成器中为 segue 分配自定义类的教程:blog.jimjh.com/…
  • 天啊!太感谢了!我已经为此搜索过,但我没有得到它!非常感谢!
  • 我不明白后退按钮的操作是如何被替换的。您只发布了如何删除最后一个。如果你执行 segue 很清楚,但是如果你用代码来做呢?我怎么知道什么时候调用你的代码?
  • 使用self.navigationController?.replaceCurrentViewControllerWith(newController, animated: true)(见上面的扩展代码)代替self.navigationController?.pushViewController(newController, animated: true)
【解决方案4】:

这可能只是重复上面所说的,但我通过修改最后一个 VewController 中的 viewDidLoad 解决了这个问题。 “theControllers”是一个 UIViewController 类型的数组。在我的例子中,我只想回到根视图控制器,所以数组只包含根视图控制器(第一个)和当前视图控制器(最后一个)。然后“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)
}

【讨论】:

    猜你喜欢
    • 2018-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多