Настройка действия кнопки «Назад» в контроллере навигации - PullRequest
175 голосов
/ 01 августа 2009

Я пытаюсь переписать действие по умолчанию кнопки «Назад» в контроллере навигации. Я предоставил цели действие на пользовательской кнопке. Странно то, что при назначении его через атрибут backbutton он не обращает на них внимания, а просто выводит текущее представление и возвращается к корню:

UIBarButtonItem *backButton = [[UIBarButtonItem alloc] 
                                  initWithTitle: @"Servers" 
                                  style:UIBarButtonItemStylePlain 
                                  target:self 
                                  action:@selector(home)];
self.navigationItem.backBarButtonItem = backButton;

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

self.navigationItem.leftBarButtonItem = backButton;

Как я могу заставить его вызвать моё собственное действие, прежде чем вернуться к корневому представлению? Есть ли способ перезаписать обратное действие по умолчанию или есть метод, который всегда вызывается при выходе из представления (viewDidUnload этого не делает)?

Ответы [ 28 ]

363 голосов
/ 10 августа 2010

Попробуйте вставить это в контроллер вида, где вы хотите обнаружить нажатие:

-(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];
}
173 голосов
/ 02 октября 2013

Я реализовал расширение UIViewController-BackButtonHandler . Для этого не нужно создавать подклассы, просто поместите это в свой проект и переопределите navigationShouldPopOnBackButton метод в UIViewController class:

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

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

42 голосов
/ 28 ноября 2009

В отличие от Амаграммера, это возможно. Вы должны создать подкласс вашего navigationController. Я объяснил все здесь (включая пример кода).

13 голосов
/ 17 декабря 2015

Swift Версия:

(из https://stackoverflow.com/a/19132881/826435)

В вашем контроллере представления вы просто соблюдаете протокол и выполняете все необходимые действия:

extension MyViewController: NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress() -> Bool {
        performSomeActionOnThePressOfABackButton()
        return false
    }
}

Затем создайте класс, скажем NavigationController+BackButton, и просто скопируйте и вставьте следующий код:

protocol NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress() -> Bool
}

extension UINavigationController {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        // Prevents from a synchronization issue of popping too many navigation items
        // and not enough view controllers or viceversa from unusual tapping
        if viewControllers.count < navigationBar.items!.count {
            return true
        }

        // Check if we have a view controller that wants to respond to being popped
        var shouldPop = true
        if let viewController = topViewController as? NavigationControllerBackButtonDelegate {
            shouldPop = viewController.shouldPopOnBackButtonPress()
        }

        if (shouldPop) {
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        } else {
            // Prevent the back button from staying in an disabled state
            for view in navigationBar.subviews {
                if view.alpha < 1.0 {
                    UIView.animate(withDuration: 0.25, animations: {
                        view.alpha = 1.0
                    })
                }
            }

        }

        return false
    }
}
5 голосов
/ 07 июля 2011

По некоторым причинам многопоточности, решение, упомянутое @HansPinckaers, было не для меня, но я нашел очень простой способ поймать прикосновение к кнопке «Назад», и я хочу закрепить это здесь на случай, если это может избежать часов обманов для кого-то еще. Трюк действительно прост: просто добавьте прозрачную UIButton в качестве подпредставления к вашему UINavigationBar и установите для него селекторы, как если бы это была настоящая кнопка! Вот пример, использующий Monotouch и C #, но перевод в target-c не должен быть слишком сложным для поиска.

public class Test : UIViewController {
    public override void ViewDidLoad() {
        UIButton b = new UIButton(new RectangleF(0, 0, 60, 44)); //width must be adapted to label contained in button
        b.BackgroundColor = UIColor.Clear; //making the background invisible
        b.Title = string.Empty; // and no need to write anything
        b.TouchDown += delegate {
            Console.WriteLine("caught!");
            if (true) // check what you want here
                NavigationController.PopViewControllerAnimated(true); // and then we pop if we want
        };
        NavigationController.NavigationBar.AddSubview(button); // insert the button to the nav bar
    }
}

Интересный факт: в целях тестирования и для определения хороших размеров моей фальшивой кнопки я установил синий цвет фона ... И он показывает позади кнопку возврата! В любом случае, он по-прежнему ловит любое прикосновение, нацеленное на оригинальную кнопку.

5 голосов
/ 25 мая 2010

Это невозможно сделать напрямую. Есть пара альтернатив:

  1. Создайте свой собственный UIBarButtonItem, который проверяется при нажатии и появляется, если тест пройден
  2. Проверка содержимого поля формы с использованием метода делегата UITextField, такого как -textFieldShouldReturn:, который вызывается после нажатия кнопки Return или Done на клавиатуре

Недостатком первого варианта является то, что стиль стрелки влево на кнопке «Назад» не может быть доступен с помощью пользовательской кнопки панели. Поэтому вы должны использовать изображение или использовать обычную кнопку стиля.

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

3 голосов
/ 18 сентября 2014

Самый простой способ

Вы можете использовать методы делегата UINavigationController. Метод willShowViewController вызывается, когда нажата кнопка «Назад» вашего VC. Все, что вы хотите, когда нажата кнопка «назад»

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
3 голосов
/ 21 августа 2015

Нашел решение, которое также сохраняет стиль кнопки возврата. Добавьте следующий метод к вашему контроллеру представления.

-(void) overrideBack{

    UIButton *transparentButton = [[UIButton alloc] init];
    [transparentButton setFrame:CGRectMake(0,0, 50, 40)];
    [transparentButton setBackgroundColor:[UIColor clearColor]];
    [transparentButton addTarget:self action:@selector(backAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.navigationController.navigationBar addSubview:transparentButton];


}

Теперь предоставьте необходимую функциональность в следующем методе:

-(void)backAction:(UIBarButtonItem *)sender {
    //Your functionality
}

Все, что он делает - это закрывает кнопку назад прозрачной кнопкой;)

3 голосов
/ 23 июля 2010

Этот метод позволяет вам изменять текст кнопки «назад», не затрагивая заголовок любого из контроллеров представления или не видя изменения текста кнопки назад во время анимации.

Добавьте это к методу init в вызывающем контроллере представления:

UIBarButtonItem *temporaryBarButtonItem = [[UIBarButtonItem alloc] init];   
temporaryBarButtonItem.title = @"Back";
self.navigationItem.backBarButtonItem = temporaryBarButtonItem;
[temporaryBarButtonItem release];
2 голосов
/ 02 ноября 2015

Решение Onegray небезопасно. Согласно официальным документам Apple, https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html, мы должны избегать этого.

"Если имя метода, объявленного в категории, совпадает с именем метода в исходном классе или метода в другой категории того же класса (или даже суперкласса), поведение не определено относительно того, какой метод реализация используется во время выполнения. Это менее вероятно, будет проблемой, если вы используете категории со своими собственными классами, но могут вызвать проблемы при использовании категорий для добавления методов в стандартные классы Cocoa или Cocoa Touch. "

...