由于我试图拒绝的一个奇怪的请求,但它不起作用,我不得不覆盖navigationBar的后退按钮。
我已经制作了一个自定义的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。每次我想在弹出后执行推送时,应用程序都会崩溃。转向NSZombie on
,发现当以编程方式弹出viewController时,其父viewController被释放。
此时,制作自定义backButton不是一个选项,因为它将丢失原生iOS 7滑动到popViewController功能。
崩溃日志:
*** -[ContactsDetailViewController performSelector:withObject:withObject:]: message sent to deallocated instance 0x1806b790
答案 0 :(得分:12)
(我以前的帖子是完全错误的。这是一个完整的重写,并提供了适当的解决方案。)
当我选择删除在转换为ARC时生成警告的代码时,我弹出了这种行为 - 我认为这些代码没有被调用。
情况如下:
如果在UINavigationController的子类中隐藏navigationBar:shouldPopItem:
,则当用户触摸NavBar的BACK按钮时,不会弹出当前视图控制器。但是,如果您直接致电popViewControllerAnimated:
,您的navigationBar:shouldPopItem:
仍会被调用,视频控制器会弹出。
以下是用户触摸BACK按钮时视图控制器无法弹出的原因:
UINavigationController有一个名为navigationBar:shouldPopItem:
的隐藏方法。当用户单击BACK按钮时调用此方法,并且当用户触摸BACK按钮时,它是通常调用popViewControllerAnimated:
的方法。
当您隐藏navigationBar:shouldPopItem:
时,不会调用超类的实现,因此不会弹出ViewController。
为什么你不应该在你的子类popViewControllerAnimated:
中调用navigationBar:shouldPopItem:
:
如果您在popViewControllerAnimated:
内拨打navigationBar:shouldPopItem:
,当您单击导航栏上的“返回”按钮时,您将看到所需的行为:您可以确定是否要弹出,以及您的视图如果你想要,控制器会弹出。
但是,如果您直接致电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;
}
答案 1 :(得分: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
来电。
编辑 - 如何添加自定义后退按钮
在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 :(得分:1)
我建议采用完全不同的方法。
为要在导航堆栈上推送的视图控制器创建基类。在viewDidLoad
方法中,将自定义按钮设置为leftBarButtonItem
的{{1}},然后添加navigationItem
,调用导航控制器的-backAction:
方法。
这样你就不会关心失去popViewControllerAnimated:
功能之类的东西,比如刷卡以及你根本不必覆盖UINavigationController
方法。
答案 3 :(得分:0)
您可能需要[super shouldPop...
而不是实际的[self popViewControllerAnimated:YES];
。
原因是UINavigationController
实现堆栈的方式是私有的,所以你应该尽可能少地处理方法调用。
无论如何,这看起来像是一个黑客。此外,用户将没有视觉线索表明您正在阻止导航操作。通过以下方式禁用按钮有什么问题:
self.navigationController.navigationItem.backBarButtonItem.enabled = NO;
答案 4 :(得分:0)
这是我对Xcode 11的@henryaz答案的解决方法:
@interface UINavigationControllerAndNavigationBarDelegate : UINavigationController<UINavigationBarDelegate>
@end
@interface CustomNavigationController : UINavigationControllerAndNavigationBarDelegate
@end
// changed this method just a bit
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
BOOL shouldPop = // detect if need to pop
if (shouldPop) {
shouldPop = [super navigationBar:navigationBar shouldPopItem:item]; // before my fix this code failed with compile error
}
return shouldPop;
}