更改导航后退按钮的目的地
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)
}
如何更改默认导航后退按钮将我带到的视图控制器?后退按钮通常会将您带回到上一个视图控制器。但是如果我想让它通过两个视图控制器返回怎么办?我的意思是我想更改后退按钮将我带到的视图控制器。我不喜欢创建自定义后退按钮。那么有没有别的办法呢?可能是链接到后退按钮或其他东西的展开转场?
您可以像这样添加自定义后退按钮来完成此操作。点击导航栏上的后退按钮添加您的自定义操作处理程序。
+ (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)
}