обратный вызов кнопки Назад в navigationController в iOS

Я нажал вид на навигационный контроллер, и когда я нажимаю кнопку "назад", он автоматически переходит к предыдущему виду. Я хочу сделать несколько вещей, когда кнопка "Назад" нажата, прежде чем выскакивать из стека. Какая функция обратного вызова кнопки "Назад"?

11 ответов


Уильяма Джокуша ответ решить эту проблему с помощью простого трюка.

-(void) viewWillDisappear:(BOOL)animated {
    if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
       // back button was pressed.  We know this is true because self is no longer
       // in the navigation stack.  
    }
    [super viewWillDisappear:animated];
}

на мой взгляд лучшее решение.

- (void)didMoveToParentViewController:(UIViewController *)parent
{
    if (![parent isEqual:self.parentViewController]) {
         NSLog(@"Back pressed");
    }
}

но он работает только с iOS5+


вероятно, лучше переопределить backbutton, чтобы вы могли обрабатывать событие до вид выскочил для таких вещей, как подтверждение пользователя.

в viewDidLoad создайте UIBarButtonItem и установите self.navigationItem.leftBarButtonItem к ней, проходящего в сельсовет

- (void) viewDidLoad
{
// change the back button to cancel and add an event handler
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:@”back”
style:UIBarButtonItemStyleBordered
target:self
action:@selector(handleBack:)];

self.navigationItem.leftBarButtonItem = backButton;
[backButton release];

}
- (void) handleBack:(id)sender
{
// pop to root view controller
[self.navigationController popToRootViewControllerAnimated:YES];

}

затем вы можете сделать такие вещи, как поднять UIAlertView для подтверждения действия, затем поп-контроллер вида и т. д.

или вместо создания новой кнопки backbutton, вы можете соответствует методам делегирования UINavigationController для выполнения действий при нажатии кнопки "Назад".


Я заканчиваю с этим решением. Когда мы нажимаем кнопку Назад, вызывается метод viewDidDisappear. мы можем проверить, вызвав селектор ismovingfromparentviewcontroller, который возвращает true. мы можем передавать данные обратно (используя Delegate).надеюсь, это поможет кому-то.

-(void)viewDidDisappear:(BOOL)animated{

    if (self.isMovingToParentViewController) {

    }
    if (self.isMovingFromParentViewController) {
       //moving back
        //pass to viewCollection delegate and update UI
        [self.delegateObject passBackSavedData:self.dataModel];

    }
}

для "BEFORE popping The view off the stack":

- (void)willMoveToParentViewController:(UIViewController *)parent{
    if (parent == nil){
        NSLog(@"do whatever you want here");
    }
}

есть более подходящий способ, чем спрашивать viewControllers. Вы можете сделать свой контроллер делегатом панели навигации с кнопкой "Назад". Вот пример. В реализации контроллера, где вы хотите обработать нажатие кнопки "Назад", сообщите ему, что он будет реализовывать протокол UINavigationBarDelegate:

@interface MyViewController () <UINavigationBarDelegate>

затем где-то в вашем коде инициализации (возможно, в viewDidLoad) сделайте свой контроллер делегатом своей навигации бар:

self.navigationController.navigationBar.delegate = self;

наконец, реализуйте метод shouldPopItem. Этот метод вызывается при нажатии кнопки "Назад". Если у вас есть несколько контроллеров или навигационных элементов в стеке, вы, вероятно, захотите проверить, какие из этих навигационных элементов выталкиваются (параметр item), так что вы только делаете свои пользовательские вещи, когда ожидаете. Вот пример:

-(BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
    NSLog(@"Back button got pressed!");
    //if you return NO, the back button press is cancelled
    return YES;
}

это правильный способ обнаружить это.

- (void)willMoveToParentViewController:(UIViewController *)parent{
    if (parent == nil){
        //do stuff

    }
}

этот метод вызывается при нажатии view. Таким образом, проверка parent==nil предназначена для выскакивания контроллера вида из стека


Если вы не можете использовать "viewWillDisappear" или аналогичный метод, попробуйте подкласс UINavigationController. Это класс заголовка:

#import <Foundation/Foundation.h>
@class MyViewController;

@interface CCNavigationController : UINavigationController

@property (nonatomic, strong) MyViewController *viewController;

@end

класс реализации:

#import "CCNavigationController.h"
#import "MyViewController.h"

@implementation CCNavigationController {

}
- (UIViewController *)popViewControllerAnimated:(BOOL)animated {
    @"This is the moment for you to do whatever you want"
    [self.viewController doCustomMethod];
    return [super popViewControllerAnimated:animated];
}

@end

С другой стороны, вам нужно связать этот viewController с вашим пользовательским NavigationController, поэтому в вашем методе viewDidLoad для вашего обычного viewController сделайте следующее:

@implementation MyViewController {
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        ((CCNavigationController*)self.navigationController).viewController = self;
    }
}

вот еще один способ, который я реализовал (не тестировал его с помощью unwind segue, но, вероятно, он не отличался бы, поскольку другие заявили в отношении других решений на этой странице), чтобы контроллер родительского вида выполнял действия до того, как дочерний VC, который он нажал, выскочил из стека представления (я использовал это на пару уровней ниже исходного UINavigationController). Это также может быть использовано для выполнения действий до того, как childVC будет вытеснен. Это имеет дополнительное преимущество работы с помощью кнопки возврата системы iOS вместо создания пользовательского UIBarButtonItem или UIButton.

  1. у вашего родителя VC принять UINavigationControllerDelegate протокол и регистрация для сообщений делегата:

    MyParentViewController : UIViewController <UINavigationControllerDelegate>
    
    -(void)viewDidLoad {
        self.navigationcontroller.delegate = self;
    }
    
  2. реализовать этот UINavigationControllerDelegate метод экземпляра в MyParentViewController:

    - (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
        // Test if operation is a pop; can also test for a push (i.e., do something before the ChildVC is pushed
        if (operation == UINavigationControllerOperationPop) {
            // Make sure it's the child class you're looking for
            if ([fromVC isKindOfClass:[ChildViewController class]]) {
                // Can handle logic here or send to another method; can also access all properties of child VC at this time
                return [self didPressBackButtonOnChildViewControllerVC:fromVC];
            }
        }
        // If you don't want to specify a nav controller transition
        return nil;
    }
    
  3. если вы укажете конкретную функцию обратного вызова в приведенном выше UINavigationControllerDelegate экземпляр метод

    -(id <UIViewControllerAnimatedTransitioning>)didPressBackButtonOnAddSearchRegionsVC:(UIViewController *)fromVC {
        ChildViewController *childVC = ChildViewController.new;
        childVC = (ChildViewController *)fromVC;
    
        // childVC.propertiesIWantToAccess go here
    
        // If you don't want to specify a nav controller transition
        return nil;
    

    }


Если вы используете раскадровку, и вы исходите из Push segue, вы также можете просто переопределить shouldPerformSegueWithIdentifier:sender:.


Это то, что он работает для меня в Swift:

override func viewWillDisappear(_ animated: Bool) {
    if self.navigationController?.viewControllers.index(of: self) == nil {
        // back button pressed or back gesture performed
    }

    super.viewWillDisappear(animated)
}