尝试在 iOS 中处理“后退”导航按钮操作
Posted
技术标签:
【中文标题】尝试在 iOS 中处理“后退”导航按钮操作【英文标题】:Trying to handle "back" navigation button action in iOS 【发布时间】:2013-09-20 09:02:36 【问题描述】:我需要检测用户何时点击导航栏上的“返回”按钮,以便在发生这种情况时执行一些操作。我正在尝试通过这种方式手动设置此类按钮的操作:
[self.navigationItem.backBarButtonItem setAction:@selector(performBackNavigation:)];
- (void)performBackNavigation:(id)sender
// Do operations
[self.navigationController popViewControllerAnimated:NO];
我首先将该代码放在视图控制器本身中,但我发现self.navigationItem.backBarButtonItem
似乎是nil
,因此我将相同的代码移至父视图控制器,后者将前者推送到导航堆栈。但我也无法让它发挥作用。我已经阅读了一些关于这个问题的帖子,其中一些人说需要在父视图控制器上设置选择器,但对我来说它无论如何都不起作用......我做错了什么?
谢谢
【问题讨论】:
将您需要的代码放在 viewWillDisappear 中是否足够好? 使用UINavigationControllerDelegate
上的方法。
@Smick 不,不幸的是,在我的场景中这还不够......
@MikeWeller 我试过了,但我无法成功
查看这个问题的答案。我找到的最佳解决方案。 ***.com/questions/1214965/…
【参考方案1】:
使用VIewWillDisappear
方法尝试此代码来检测NavigationItem的后退按钮的按下:
-(void) viewWillDisappear:(BOOL)animated
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound)
// Navigation button was pressed. Do some stuff
[self.navigationController popViewControllerAnimated:NO];
[super viewWillDisappear:animated];
或者还有另一种方法可以获得导航返回按钮的操作。
为后退按钮的 UINavigationItem 创建自定义按钮。
例如:
在 ViewDidLoad 中:
- (void)viewDidLoad
[super viewDidLoad];
UIBarButtonItem *newBackButton = [[UIBarButtonItem alloc] initWithTitle:@"Home" style:UIBarButtonItemStyleBordered target:self action:@selector(home:)];
self.navigationItem.leftBarButtonItem=newBackButton;
-(void)home:(UIBarButtonItem *)sender
[self.navigationController popToRootViewControllerAnimated:YES];
斯威夫特:
override func willMoveToParentViewController(parent: UIViewController?)
if parent == nil
// Back btn Event handler
【讨论】:
我认为您在 viewWillDisappear 中不需要 [self.navigationController popViewControllerAnimated:NO]。 我认为这种方法不再适用于 ios 8,因为 self.navigationController.viewControllers 只包含一个元素,== self @Sébastien Stormacq 为什么这么说?它适用于 iOS 8。 如果你在 ViewWillDisappear 中调用它,它不会在点击返回按钮时被调用。每当弹出 VC 或推送新 VC 时都会调用它【参考方案2】:也许这个答案不符合您的解释,但问题标题。当您想知道您何时点击了UINavigationBar
上的返回按钮时,它很有用。
在这种情况下,您可以使用UINavigationBarDelegate
协议并实现以下方法之一:
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;
- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item;
当didPopItem
方法被调用时,这是因为你要么点击了返回按钮,要么你使用了[UINavigationBar popNavigationItemAnimated:]
方法并且导航栏确实弹出了该项目。
现在,如果您想知道是什么动作触发了didPopItem
方法,您可以使用一个标志。
使用这种方法,我不需要手动添加带有箭头图像的左栏按钮项,以使其类似于 iOS 后退按钮,并且能够设置我的自定义目标/操作。
我们来看一个例子:
我有一个视图控制器,它有一个页面视图控制器和一个自定义页面指示器视图。我还使用自定义 UINavigationBar 来显示标题以了解我在哪个页面上,并使用后退按钮返回上一页。而且我还可以在页面控制器上滑动到上一页/下一页。
#pragma mark - UIPageViewController Delegate Methods
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
if( completed )
//...
if( currentIndex > lastIndex )
UINavigationItem *navigationItem = [[UINavigationItem alloc] initWithTitle:@"Some page title"];
[[_someViewController navigationBar] pushNavigationItem:navigationItem animated:YES];
[[_someViewController pageControl] setCurrentPage:currentIndex];
else
_autoPop = YES; //We pop the item automatically from code.
[[_someViewController navigationBar] popNavigationItemAnimated:YES];
[[_someViewController pageControl] setCurrentPage:currentIndex];
然后我实现 UINavigationBar 委托方法:
#pragma mark - UINavigationBar Delegate Methods
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
if( !_autoPop )
//Pop by back button tap
else
//Pop from code
_autoPop = NO;
return YES;
在这种情况下,我使用了shouldPopItem
,因为弹出是动画的,我想立即处理后退按钮,而不是等到转换完成。
【讨论】:
【参考方案3】:设置UINavigationBar的delegate,然后使用:
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
//handle the action here
【讨论】:
如果您使用UINavigationController
管理导航栏,然后尝试设置委托会导致异常:“*** 由于未捕获的异常 'NSInternalInconsistencyException' 而终止应用程序,原因:'无法手动在控制器管理的 UINavigationBar 上设置委托。'"。 UINavigationController
是代表。这意味着您可以对控制器进行子类化并覆盖 UINavigationBarDelegate
方法(可能调用 super)。
但是不能直接调用super,因为UINavigationController
不公开符合UINavigationBarDelegate
,导致编译错误!可能有使用UINavigationControllerDelegate
的解决方案。【参考方案4】:
斯威夫特
override func didMoveToParentViewController(parent: UIViewController?)
if parent == nil
//"Back pressed"
【讨论】:
此解决方案的唯一问题是,如果您滑动返回并改变主意,这将被触发。【参考方案5】:这是 dadachi's 答案的 Objective-C 版本:
目标-C
- (void)didMoveToParentViewController:(UIViewController *)parent
if (parent == NULL)
NSLog(@"Back Pressed");
【讨论】:
【参考方案6】:didMoveToParentViewController
的问题在于,一旦父视图再次完全可见,它就会被调用,因此如果您需要在此之前执行一些任务,它将无法工作。
而且它不适用于驱动动画手势。
使用willMoveToParentViewController
效果更好。
Objective-c
- (void)willMoveToParentViewController:(UIViewController *)parent
if (parent == NULL)
// ...
斯威夫特
override func willMoveToParentViewController(parent: UIViewController?)
if parent == nil
// ...
【讨论】:
【参考方案7】:设置 UINavigationControllerDelegate 并实现这个委托函数(Swift):
func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool)
if viewController is <target class>
//if the only way to get back - back button was pressed
【讨论】:
【参考方案8】:其他解决方案都不适合我,但这确实有效:
创建自己的 UINavigationController 子类,使其实现 UINavigationBarDelegate(无需手动设置导航栏的委托),添加 UIViewController 扩展,定义在按下后退按钮时调用的方法,然后在你的 UINavigationController 子类:
func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool
self.topViewController?.methodToBeCalledOnBackButtonPress()
self.popViewController(animated: true)
return true
【讨论】:
您能否扩展您的答案并展示如何在视图控制器中准确使用它。【参考方案9】:使用自定义的UINavigationController
子类,它实现了shouldPop
方法。
在斯威夫特中:
class NavigationController: UINavigationController, UINavigationBarDelegate
var shouldPopHandler: (() -> Bool)?
func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool
if let shouldPopHandler = self.shouldPopHandler, !shouldPopHandler()
return false
self.popViewController(animated: true) // Needed!
return true
设置后,您的shouldPopHandler()
将被调用以决定控制器是否弹出。如果没有设置,它会像往常一样弹出。
最好禁用UINavigationController
s interactivePopGestureRecognizer
,否则手势不会调用您的处理程序。
【讨论】:
【参考方案10】:在 Swift 4 或更高版本中:
override func didMove(toParent parent: UIViewController?)
if parent == nil
//"Back pressed"
【讨论】:
以上是关于尝试在 iOS 中处理“后退”导航按钮操作的主要内容,如果未能解决你的问题,请参考以下文章