PopViewController奇怪的行为
由于一个奇怪的请求,我试图拒绝,但没有奏效,我不得不重写导航栏的后面button。
我已经做了一个自定义的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
所有工作正常,除非我以编程方式popupviewController。 每当我想在popup窗口后执行推送时,应用程序都会崩溃。 打开NSZombie on
,显示当以编程方式popupviewController时,其父级viewController被释放。 在这一点上,做一个自定义backButton不是一个选项,因为它将失去原生的iOS 7滑动到popViewControllerfunction。
崩溃日志:
*** -[ContactsDetailViewController performSelector:withObject:withObject:]: message sent to deallocated instance 0x1806b790
我不是100%肯定的,但我不认为你应该实际上popup在该委托方法的视图控制器。
“应该”委托方法通常不会做什么。 他们只是断言是否应该做什么。
改变你的方法…
- (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
调用。
编辑 – 如何添加自定义后退button
在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; }
现在,只要你想设置自定义后退button只是使用…
UIBarButtonItem *backButton = [UIBarButtonItem customBackButtonWithTarget:self action:@selector(backButtonPressed)];
(我以前的post是完全错误的,这是一个完整的重写和一个适当的解决scheme。)
当我select删除一些生成警告的代码时,我已经popup了这个行为,当我转换到ARC – 我认为没有被调用的代码。
情况如下:
如果在UINavigationController的子类中影响navigationBar:shouldPopItem:
那么当用户触摸NavBar的BACKbutton时,当前视图控制器将不会popup。 但是,如果直接调用popViewControllerAnimated:
您的navigationBar:shouldPopItem:
仍将被调用,并且视图控制器将popup。
这就是当用户触摸BACKbutton时视图控制器无法popup的原因:
UINavigationController有一个名为navigationBar:shouldPopItem:
的隐藏方法navigationBar:shouldPopItem:
当用户点击BACKbutton时,这个方法被调用,并且这是通常调用popViewControllerAnimated:
的方法popViewControllerAnimated:
当用户触摸BACKbutton时。
当你navigationBar:shouldPopItem:
,超级类的实现不被调用,因此ViewController没有popup。
为什么你不应该在你的子类的navigationBar:shouldPopItem:
调用popViewControllerAnimated:
:
如果您在navigationBar:shouldPopItem:
popViewControllerAnimated:
内调用popViewControllerAnimated:
那么当您单击NavBar上的BACKbutton时,您将看到所需的行为:您可以确定是否要popup窗口,并且视图控制器会在需要时popup。
但是,如果直接调用popViewControllerAnimated:
则最终会popup两个视图控制器:一个来自您直接调用popViewControllerAnimated:
另一个来自您添加到navigationBar:shouldPopItem:
内的调用。
我认为是安全的解决scheme:
您的自定义导航控制器应该这样声明:
@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; }
我会build议一个完全不同的方法。
为您在导航堆栈上推送的视图控制器创build一个基类。 在viewDidLoad
方法中,将自定义button设置为navigationItem
的leftBarButtonItem
,并添加一个-backAction:
它调用导航控制器的popViewControllerAnimated:
方法。
这样你就不会在意像丢失UINavigationController
function,比如滑动到popup,你不需要重写navigationBar:shouldPopItem:
方法。
你可能需要做[super shouldPop...
而不是实际[self popViewControllerAnimated:YES];
。
原因在于UINavigationController
实现堆栈的方式是私有的,所以你应该尽可能less地使用方法调用。
无论如何,这看起来像一个黑客。 而且,用户将没有视觉线索阻止导航操作。 通过以下方式禁用button有什么问题?
self.navigationController.navigationItem.backBarButtonItem.enabled = NO;