PopViewController странное поведение

Из-за странного запроса, который я попытался отклонить, но это не сработало, мне пришлось переопределить кнопку назад навигационной панели.

Я создал пользовательский подкласс UINavigationController и взломал - (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item.

Вот мой код:

@interface CustomUINavigationController ()

@end

@implementation CustomUINavigationController


#pragma mark - UINavigationBar delegate methods

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

    if ([[self.viewControllers lastObject] isKindOfClass:[ViewController1 class]]) {
        ViewController1 *vc1 = (ViewController1 *)[self.viewControllers lastObject];
        [vc1 handleBackAction];
        if (vc1.canPopVC == YES) { 
            [self popViewControllerAnimated:YES];
            return YES;
        } else {
            return NO;
        }
    }

    [self popViewControllerAnimated:YES];
    return YES;
}

@end

Все работает отлично, за исключением случаев, когда программным обеспечением popController. Приложение разбилось каждый раз, когда я хотел выполнить push после упомянутого pop. Повернув NSZombie on, выяснилось, что при программном обращении к viewController его родительский viewController освобождается. На данный момент создание пользовательского backButton не является вариантом, так как оно потеряет собственный iOS 7 для функции popViewController.

Журнал сбоев:

*** -[ContactsDetailViewController performSelector:withObject:withObject:]: message sent to deallocated instance 0x1806b790

Ответ 1

Я не уверен на 100%, но я не думаю, что вы действительно должны появляться в контроллере представления в этом методе делегата.

"должен" делегировать методы, как правило, ничего не делают. Они просто утверждают, что что-то должно или не должно быть сделано.

Измените свой метод на этот...

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

    if ([[self.viewControllers lastObject] isKindOfClass:[ViewController1 class]]) {
        ViewController1 *vc1 = (ViewController1 *)[self.viewControllers lastObject];
        [vc1 handleBackAction];
        if (vc1.canPopVC == YES) { 
            return YES;
        } else {
            return NO;
        }
    }

    return YES;
}

И посмотрите, работает ли он.

Все, что я сделал, это удалить вызовы popViewController.

EDIT - Как добавить пользовательскую кнопку назад

В категории на UIBarButtonItem...

+ (UIBarButtonItem *)customBackButtonWithTarget:(id)target action:(@SEL)action
{
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    [button setBackgroundImage:[UIImage imageNamed:@"Some image"] forState:UIControlStateNormal];
    [button setTitle:@"Some Title" forState:UIControlStateNormal];
    [button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];

    UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:button];

    return barButtonItem;
}

Теперь, когда вы хотите установить пользовательскую кнопку возврата, просто используйте...

UIBarButtonItem *backButton = [UIBarButtonItem customBackButtonWithTarget:self action:@selector(backButtonPressed)];

Ответ 2

(Мой предыдущий пост был совершенно неправильным. Это полная переработка с соответствующим решением.)

У меня появилось это поведение, когда я решил удалить код, генерирующий предупреждение, когда я конвертировал в ARC-код, который, как я думал, не вызывался.

Здесь ситуация:

Если вы теневой navigationBar:shouldPopItem: в подклассе UINavigationController, тогда текущий контроллер просмотра НЕ будет отображаться, когда пользователь коснется кнопки NavBar BACK. Однако, если вы вызываете popViewControllerAnimated: напрямую, ваш navigationBar:shouldPopItem: по-прежнему будет вызываться, и контроллер вида появится.

Вот почему контроллер представления не может появиться, когда пользователь коснется кнопки BACK:

UINavigationController имеет скрытый метод navigationBar:shouldPopItem:. Этот метод вызывается, когда пользователь нажимает кнопку BACK, и это метод, который обычно вызывает popViewControllerAnimated:, когда пользователь прикасается к кнопке BACK.

Когда вы теневое navigationBar:shouldPopItem:, реализация суперкласса не вызывается, и, следовательно, ViewController не появляется.

Почему вы не должны называть popViewControllerAnimated: в своем подклассе <<20 > :

Если вы вызываете popViewControllerAnimated: внутри navigationBar:shouldPopItem:, вы увидите поведение, которое вы желаете, когда вы нажимаете кнопку BACK на NavBar: вы можете определить, хотите ли вы поп-музыку или нет, и контроллер вашего вида появится, если вы хотите его.

Но, если вы вызываете popViewControllerAnimated: напрямую, вы в конечном итоге вытащите два контроллера вида: один из вашего прямого вызова на popViewControllerAnimated:, а другой - из вызова, добавленного в navigationBar:shouldPopItem:.

Что я считаю безопасным решением:

Пользовательский навигационный контроллер должен быть объявлен следующим образом:

@interface CustomNavigationController : UINavigationController <UINavigationBarDelegate> 
{
    // .. any ivars you want
}
@end

Ваша реализация должна содержать код, который выглядит примерно так:

// Required to prevent a warning for the call [super navigationBar:navigationBar shouldPopItem:item]
@interface UINavigationController () <UINavigationBarDelegate>
@end


@implementation CustomNavigationController

- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
    BOOL rv = TRUE;

    if ( /* some condition to determine should NOT pop */ )
    {
        // we won't pop
        rv = FALSE;

        // extra code you might want to execute ...
    } else
    {
        // It not documented that the super implements this method, so we're being safe
        if ([[CustomNavigationController superclass]
                instancesRespondToSelector:@selector(navigationBar:shouldPopItem:)])
        {
            // Allow the super class to do its thing, which includes popping the view controller 
            rv = [super navigationBar:navigationBar shouldPopItem:item];

        }
    }

    return rv;
}

Ответ 3

Я бы предложил совершенно другой подход.

Создайте базовый класс для контроллеров представления, которые вы нажимаете на стек навигации. В методе viewDidLoad задайте свою пользовательскую кнопку как leftBarButtonItem navigationItem и добавьте -backAction:, который вызывает метод popViewControllerAnimated: навигационного контроллера.

Таким образом вам не понадобятся такие вещи, как потеря функциональности UINavigationController, например, салфетки для попса, и вам не придется переопределять метод navigationBar:shouldPopItem: вообще.

Ответ 4

Вероятно, вам нужно сделать [super shouldPop... вместо фактического [self popViewControllerAnimated:YES];.

Причина, заключающаяся в том, что способ UINavigationController реализует стек, является конфиденциальным, поэтому вы должны как можно меньше взаимодействовать с вызовами метода.

Во всяком случае, это выглядит как взлома. Более того, у пользователя не будет визуальной подсказки, что вы блокируете действие навигации. Неправильно отключить кнопку с помощью:

self.navigationController.navigationItem.backBarButtonItem.enabled = NO;