2013-12-02 4 views
1

Из-за странного запроса, который я попытался отклонить, но это не сработало, мне пришлось переопределить кнопку назад в навигационной панели.Потрясающее поведение 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 

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

Краш журнала:

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

Пожалуйста, включите журнал сбоев. Кроме того, я не думаю, что вы на самом деле должны быть здесь. Вы должны просто сказать «ДА» или «НЕТ», если он должен появиться. – Fogmeister

ответ

2

Я не 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)]; 
+1

Если я удалю вызовы popViewController и коснитесь backButton, появятся только всплывающие окна navigationBar, но тот же viewController останется на экране. –

+0

Тем не менее, вы не должны вводить контроллер в этот метод. Вам нужно найти, как правильно использовать его и код. Почему вы все-таки подклассифицируете UINavController? Наверняка вы можете просто настроить пользовательскую кнопку «Назад»? Я сделал это, используя категорию в прошлом. Тогда только одна строка изменит кнопку «Назад».Не требуется подклассов. – Fogmeister

+0

Ваш ответ - самый «правильный способ» сделать что-то и, на мой взгляд, хорошо. Хотя я решил свою проблему по-другому, я буду считать ее правильной. –

0

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

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

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

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

Предлагаю совершенно другой подход.

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

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

11

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

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

Вот ситуация:

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

Вот почему-контроллер не может выскочить, когда пользователь нажимает кнопку НАЗАД:

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

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

Почему вы не должны называть popViewControllerAnimated: в пределах подкласса navigationBar:shouldPopItem::

Если вы звоните popViewControllerAnimated: в navigationBar:shouldPopItem:, вы увидите поведение, которое вы хотите, когда вы нажимаете кнопку НАЗАД на 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's 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; 
} 
+0

Отличная запись и, вероятно, должен быть принят. – rjstelling

+0

Работает отлично и наилучшим образом до сих пор, должен быть принятым ответом! Спасибо –

+1

Это также довольно хороший способ реализовать его без частного API-интерфейса ... http://www.hkwebentrepreneurs.com/2013/11/ios-prevent-back-button-navigating-to.html –

Смежные вопросы