Спроба обробити дію кнопки "Назад" в iOS


76

Мені потрібно визначити, коли користувач натискає кнопку "назад" на панелі навігації, щоб виконати деякі операції, коли це відбувається. Я намагаюся вручну встановити дію на таку кнопку, таким чином:

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

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

   [self.navigationController popViewControllerAnimated:NO];
}

Спочатку я розмістив цей код у самому контролері перегляду, але виявив, що це self.navigationItem.backBarButtonItemздавалося nil, тому я перемістив той самий код до батьківського контролера перегляду, який штовхає перший у стек навігації. Але я не можу змусити це працювати. Я прочитав деякі дописи щодо цієї проблеми, і деякі з них сказали, що селектор потрібно встановити на батьківському контролері подання, але для мене це все одно не працює ... Що я можу зробити неправильно?

Дякую


чи було б досить добре розмістити потрібний код у viewWillDisappear?
DogCoffee

1
Використовуйте методи на UINavigationControllerDelegate.
Mike Weller

@Smick Ні, на жаль, цього не вистачить у моєму сценарії ...
AppsDev

@MikeWeller Я намагався, але не міг змусити це працювати
AppsDev

Перевірте відповідь у цьому питанні. Найкраще рішення, яке я знайшов. stackoverflow.com/questions/1214965/…
Джеймс Паркер,

Відповіді:


132

Спробуйте цей код, використовуючи 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];
}

АБО існує інший спосіб отримати дію кнопки Navigation BAck.

Створити власну кнопку для 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
    }
}

3
Я не думаю, що вам потрібен [self.navigationController popViewControllerAnimated: NO] in viewWillDisappear.
Роберт Кан

2
Я думаю, що цей метод більше не працює з iOS 8, оскільки self.navigationController.viewControllers містить лише один елемент, == self
Себастьян Стормак

1
É Себастьян Стормак Чому ти так кажеш? Це працює в iOS 8.
蘇健豪

1
Якщо ви викликаєте це в ViewWillDisappear, воно не буде викликане лише при натисканні кнопки "Назад". Він буде викликаний щоразу, коли виконується ВК або коли натискається новий ВК
NSNoob

41

Стрімкий

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

5
Єдина проблема з цим рішенням - якщо ви проведете пальцем, щоб повернутися назад, і передумаєте, це спрацює.
chris P

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працює краще.

Завдання-с

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

Стрімкий

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


3

Встановіть делегат UINavigationBar, а потім використовуйте:

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

2
Якщо ви використовуєте a UINavigationControllerдля управління панеллю навігації, тоді спроба встановити делегата спричиняє виняток: "*** Завершення роботи програми через невпійманий виняток" NSInternalInconsistentcyException ", причина:" Не вдається вручну встановити делегата на UINavigationBar, керованому контролером. '". Це UINavigationControllerделегат. А це означає, що ви можете підклас контролера і замінити UINavigationBarDelegateметоди (можливо, виклик супер).
Geoff Hackworth

Але ви не можете безпосередньо зателефонувати супер, оскільки UINavigationControllerце не відповідає загальнодоступним UINavigationBarDelegate, що призводить до помилки компілятора! Може бути рішення, використовуючи UINavigationControllerDelegate.
Geoff Hackworth

3

Жодне з інших рішень не працювало для мене, але це робить:

Створіть свій власний підклас UINavigationController, змусьте його реалізувати UINavigationBarDelegate (не потрібно вручну встановлювати делегата панелі навігації), додайте розширення UIViewController, яке визначає метод, який буде викликаний при натисканні кнопки "Назад", а потім реалізуйте цей метод у своєму підкласі UINavigationController :

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

Чи можете ви розширити свою відповідь і показати, як саме використовувати її в контролері перегляду.
zeeshan

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()буде зателефоновано, щоб вирішити, буде контролер спливаючим чи ні. Якщо не встановлено, він просто вискакує, як зазвичай.

Це гарна ідея вимкнути UINavigationControllers, interactivePopGestureRecognizerоскільки за допомогою жесту інакше ваш дзвінок не буде викликати.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.