UINavigationController: Как отменить событие кнопки "Назад"?

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

- (void)viewWillDisappear:(BOOL)animated {
    if (backBtnPressed) {
        UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:@"Question" message:@"Do you really want to go back?" delegate:self cancelButtonTitle:@"No" otherButtonTitles: @"Yes", nil] autorelease];
        [alert show];   
    }
}

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
    if (buttonIndex == 0) {
        // don't go back!
        // cancel the back button event
    }
    else if (buttonIndex == 1) {
        // go back
    }
}

Но с этим кодом у меня нет шансов! Я не могу остановить событие кнопки "Назад" , не так ли?

Должен ли я написать свою собственную кнопку возврата и установить ее как leftBarButtonItem? Или есть кто-нибудь с отличной идеей?: -)

Спасибо за вашу помощь!

Ответ 1

viewWillDisappear - это метод делегата для события, который будет исчезать, и ничего не может сделать разработчик! Если бы вы могли, это был бы метод делегата viewShouldDisappear.

Итак, я думаю, что единственный способ, как вы предлагаете, использовать пользовательский leftBarButtonItem.

Ответ 2

Мой ответ из другого потока соответствует этому вопросу. Поэтому я перепечатываю его здесь:

Я реализовал расширение UIViewController-BackButtonHandler. Ему не нужно ничего подклассифицировать, просто поместите в проект и переопределите метод navigationShouldPopOnBackButton в классе UIViewController:

-(BOOL) navigationShouldPopOnBackButton {
    if(needsShowConfirmation) {
        // Show confirmation alert
        // ...
        return NO; // Ignore 'Back' button this time
    }
    return YES; // Process 'Back' button click and Pop view controller
}

Загрузить пример приложения.

Ответ 3

Что вам нужно сделать, так это использовать делегат панели навигации, а не навигационный контроллер.

- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPushItem:(UINavigationItem *)item; // called to push. return NO not to.
- (void)navigationBar:(UINavigationBar *)navigationBar didPushItem:(UINavigationItem *)item;    // called at end of animation of push or immediately if not animated
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;  // same as push methods
- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item;

Ответ 4

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

Как указывали многие, метод, приведенный ниже в UINavigationBarDelegate, является ключом к реализации этой функции.

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

Многие из них имеют подклассы UINavigationController и реализовали вышеописанный метод, чтобы упростить его использование без прямого доступа к UINavigationBar.

К сожалению, все еще остаются некоторые проблемы.

  • Жесткий жест не вызывает этот метод.
  • Хотя это кажется необходимым, сообщается о сбоях при вызове popViewControllerAnimated: в этом методе.
  • Кнопка "Назад" остается недоступной, когда поп отменяется.

Проведите назад жест

Нам нужно перехватить жест, установив делегат, как это сделано в fooobar.com/questions/367162/....

Если UINavigationController является подклассом, это будет:

self.interactivePopGestureRecognizer.delegate = self

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

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer

Соблюдайте осторожность при изменении свойства делегирования, поскольку он изменяется после вызова инициализатора.

Не вызывать popViewControllerAnimated:

Хотя недокументировано, вызов popViewControllerAnimated: можно избежать, как в fooobar.com/questions/25276/....

Он включает вызов navigationBar:shouldPopItem: из UINavigationController (из подкласса).

Кнопка "Назад"

Хотя это может быть небольшая деталь (особенно если вы создали свою собственную кнопку "Назад" ), есть простое решение (написанное мной:) fooobar.com/questions/367165/...

Вам нужно только установить свойство ДА и НЕТ.

auto item = navigationBar.topItem;
item.hidesBackButton = YES;
item.hidesBackButton = NO;

Ответ 5

Вы можете использовать пользовательскую кнопку с графикой, которая выглядит точно так же, как кнопка "Назад", и создайте пользовательское представление leftBarButtonItem как UIButton с этой графикой. Добавьте целевую кнопку self к вашей кнопке с помощью настраиваемого селектора back: и введите там свое предупреждение. Если пользователь нажимает "да", чтобы выйти из этого контроллера просмотра, если нет - ничего не делать. Трюк здесь - кнопка, которая выглядит точно так же, как кнопка назад на панели навигации.

Ответ 6

Лучше, если вы сделаете свою собственную кнопку назад и сделаете ее левой кнопкой навигационного контроллера. Это может определенно помочь выполнить любое действие

Ответ 7

Метод

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

делает то, что вы хотите. К сожалению, мы не должны делегировать UINavigationBar нашим собственным объектам: (

Apple Documentation заявляет:

... Кроме того, объект контроллера навигации автоматически присваивает себя как делегат своего объекта UINavigationBar и не позволяет другим объектам изменять это отношение....

Один/The? способ сделать то, что вы хотите, - это ввести свою собственную кнопку. В этом методе вы выполняете свои тесты и вызываете

[self.navigationController popViewControllerAnimated:true];

если пользователю разрешено вернуться назад.

Ответ 8

Если вы ищете способ сделать это в Swift на iOS 10, вы можете создать пользовательский UINavigationController, а затем расширение UINavigationBarDelegate:

class MyNavigationController : UINavigationController {

}
extension MyNavigationController : UINavigationBarDelegate {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        return false
    }
}