PopViewController的奇怪行为



由于一个奇怪的请求,我试图拒绝,但没有成功,我不得不覆盖导航栏的后退按钮。

我制作了一个自定义的UINavigationController子类,并破解了CCD_ 1方法。

这是我的代码:

@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

当我选择删除一些代码时,我出现了这种行为,当我转换到ARC时,生成了一个警告——我认为没有调用的代码。

情况如下:

如果在UINavigationController的子类中隐藏navigationBar:shouldPopItem:,则当用户触摸NavBar的BACK按钮时,当前视图控制器将不会弹出。但是,如果您直接调用popViewControllerAnimated:,您的navigationBar:shouldPopItem:仍将被调用,并且视图控制器将弹出。

以下是用户触摸BACK按钮时视图控制器无法弹出的原因:

UINavigationController有一个名为navigationBar:shouldPopItem:的隐藏方法。此方法是在用户单击BACK按钮时调用的,也是在用户触摸BACK按钮后通常调用popViewControllerAnimated:的方法。

当您对navigationBar:shouldPopItem:进行阴影处理时,不会调用超类的实现,因此不会弹出ViewController。

为什么不应该在子类"- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item0"中调用popViewControllerAnimated:

如果您在navigationBar:shouldPopItem:中调用popViewControllerAnimated:,当您单击导航栏上的返回按钮时,您将看到您想要的行为:您可以确定是否要弹出,如果您想要,您的视图控制器将弹出。

但是,如果直接调用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;
}

我不能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)];

我建议采用完全不同的方法。

为正在导航堆栈上推动的视图控制器创建基类。在viewDidLoad方法中,将自定义按钮设置为navigationItemleftBarButtonItem,并添加一个调用导航控制器的popViewControllerAnimated:方法的-backAction:

这样,您就不必担心UINavigationController的功能丢失,比如滑动弹出,也不必覆盖navigationBar:shouldPopItem:方法。

您可能需要执行[super shouldPop...而不是实际的[self popViewControllerAnimated:YES];

原因是UINavigationController实现堆栈的方式是私有的,所以应该尽可能少地处理方法调用。

不管怎样,这看起来像是黑客。此外,用户将没有视觉线索表明您正在阻止导航操作。通过禁用按钮有什么问题

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

这是我对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;
    }

相关内容

  • 没有找到相关文章

最新更新