尝试处理 iOS 中的“后退”导航按钮操作

问题描述 投票:0回答:11

我需要检测用户何时点击导航栏上的“后退”按钮,以便在发生这种情况时执行一些操作。我正在尝试手动设置此类按钮的操作,这样:

[self.navigationItem.backBarButtonItem setAction:@selector(performBackNavigation:)];

- (void)performBackNavigation:(id)sender
{
   // Do operations

   [self.navigationController popViewControllerAnimated:NO];
}

我首先将该代码放置在视图控制器本身中,但我发现

self.navigationItem.backBarButtonItem
似乎是
nil
,所以我将相同的代码移至父视图控制器,这将前者推送到导航堆栈。但我也无法让它发挥作用。我读过一些关于这个问题的帖子,其中一些说选择器需要在父视图控制器上设置,但对我来说它无论如何都不起作用......我可能做错了什么?

谢谢

ios uinavigationcontroller uinavigationbar selector
11个回答
131
投票

使用

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

41
投票

斯威夫特

override func didMoveToParentViewController(parent: UIViewController?) {
    if parent == nil {
        //"Back pressed"
    }
}

18
投票

也许这个答案不符合你的解释,但符合问题标题。当您想知道何时点击了

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
,因为弹出是动画的,我想立即处理后退按钮,而不是等到转换完成。


12
投票

didMoveToParentViewController
的问题在于,一旦父视图再次完全可见,它就会被调用,因此如果您需要在此之前执行一些任务,它将无法工作。

并且它不适用于驱动动画手势。 使用

willMoveToParentViewController
效果更好。

Objective-c

- (void)willMoveToParentViewController:(UIViewController *)parent{
    if (parent == NULL) {
        // ...
    }
}

斯威夫特

override func willMoveToParentViewController(parent: UIViewController?) {
    if parent == nil {
        // ...  
    }
}

6
投票

这是dadachi的Objective-C版本答案:

目标-C

- (void)didMoveToParentViewController:(UIViewController *)parent{
    if (parent == NULL) {
        NSLog(@"Back Pressed");
    }
}

3
投票

设置UINavigationBar的delegate,然后使用:

- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
    //handle the action here
}

3
投票

其他解决方案都不适合我,但这个解决方案:

创建您自己的 UINavigationController 子类,使其实现 UINavigationBarDelegate (无需手动设置导航栏的委托),添加 UIViewController 扩展来定义按下后退按钮时调用的方法,然后在 UINavigationController 中实现此方法子类:

func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
    self.topViewController?.methodToBeCalledOnBackButtonPress()
    self.popViewController(animated: true)
    return true
}

3
投票

在 Swift 4 或更高版本中:

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

2
投票

设置 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
    }
}

1
投票

使用自定义

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
是个好主意,否则手势不会调用您的处理程序。


0
投票

backAction

的帮助下,从 iOS16+ 开始,这似乎是可能的

以下是文档的内容:

如果导航栏中已出现后退按钮,则设置此属性将替换其操作,而不修改其外观。

因此,在您的

viewDidLoad
或适当的地方,您可以添加以下代码:

navigationItem.backAction = UIAction { [weak self] _ in
    // Handle your other logic here for back button tap
    self?.navigationController?.popViewController(animated: true)
}
© www.soinside.com 2019 - 2024. All rights reserved.