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;