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

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

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


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


[self.navigationController popViewControllerAnimated:NO];
}

我首先将这些代码放在视图控制器本身,但是我发现 self.navigationItem.backBarButtonItem似乎是 nil,所以我将同样的代码移动到父视图控制器,这将前者推到导航堆栈。但我也没办法。我读过一些关于这个问题的文章,其中一些说选择器需要在父视图控制器上设置,但是对我来说它无论如何都不能工作... ... 我能做错什么呢?

谢谢

122961 次浏览

使用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];
}

OR There is another way to get Action of the Navigation BAck button.

Create Custom button for UINavigationItem of back button .

For Ex:

In 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];
}

Swift :

override func willMoveToParentViewController(parent: UIViewController?)
{
if parent == nil
{
// Back btn Event handler
}
}

Perhaps this answers doesn't fit your explanation but question title. It's useful when you are trying to know when you tapped the back button on an UINavigationBar.

In this case you can use UINavigationBarDelegate protocol and implement one of this methods:

- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;
- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item;

When didPopItem method is called, it's because you either tapped the back button or you used [UINavigationBar popNavigationItemAnimated:] method and the navigation bar did pop the item.

Now, if you want to know what action triggered the didPopItem method you can use a flag.

With this approach I don't need to manually add a left bar button item with an arrow image in order to make it similar to iOS back button, and be able to set my custom target/action.


Let's see an example:

I have a view controller that has a page view controller, and a custom page indicator view. I'm also using a custom UINavigationBar to display a title to know on what page am I and the back button to go back to the previous page. And I also can swipe to previous/next page on page controller.

#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];
}
}


}

So then I implement UINavigationBar delegate methods:

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

In this case I used shouldPopItem because the pop is animated and I wanted to handle the back button immediately and not to wait until transition is finished.

Set the UINavigationBar's delegate, and then use:

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

Swift

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

This is Objective-C version of dadachi's Answer :

Objective-C

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

The problem with didMoveToParentViewController it's that it gets called once the parent view is fully visible again so if you need to perform some tasks before that, it won't work.

And it doesn't work with the driven animation gesture. Using willMoveToParentViewController works better.

Objective-c

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

Swift

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

Set the UINavigationControllerDelegate and implement this delegate func (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
}
}

None of the other solutions worked for me, but this does:

Create your own subclass of UINavigationController, make it implement the UINavigationBarDelegate (no need to manually set the navigation bar's delegate), add a UIViewController extension that defines a method to be called on a back button press, and then implement this method in your UINavigationController subclass:

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

Use a custom UINavigationController subclass, which implements the shouldPop method.

In Swift:

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

When set, your shouldPopHandler() will be called to decide whether the controller will be pop or not. When not set it will just get popped as usual.

It is a good idea to disable UINavigationControllers interactivePopGestureRecognizer as the gesture won't call your handler otherwise.

In Swift 4 or above:

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