尝试在 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() 将被调用以决定控制器是否弹出。如果没有设置,它会像往常一样弹出。

最好禁用UINavigationControllers interactivePopGestureRecognizer,否则手势不会调用您的处理程序。

【讨论】:

【参考方案10】:

在 Swift 4 或更高版本中:

override func didMove(toParent parent: UIViewController?) 
    if parent == nil 
        //"Back pressed"
    

【讨论】:

以上是关于尝试在 iOS 中处理“后退”导航按钮操作的主要内容,如果未能解决你的问题,请参考以下文章

在导航控制器中设置后退按钮的操作

如何检测由滑动触发的后退导航并在iOS上将其停止?

禁用后退按钮导航操作

导航栏项目中未显示标题和后退按钮(iOS 7)

UWP-标题栏”后退“按钮

处理 Windows Phone 应用中的“后退”按钮 (XAML)