美文网首页
iOS自定义leftBarButtonItem、backBarB

iOS自定义leftBarButtonItem、backBarB

作者: 魔力双鱼 | 来源:发表于2018-12-05 15:04 被阅读0次

    搞清leftBarButtonItem、backBarButtonItem

    我们在A视图设置的代码,系统是如何实现从A视图push到B视图后,显示在B视图的?


    push说明图.png

    显示原理:

    1、如果B视图有一个自定义的左侧按钮(leftBarButtonItem),则会显示这个自定义按钮;
    2、如果B没有自定义按钮,但是A视图的backBarButtonItem属性有自定义项,则显示这个自定义项;
    3、如果前2条都没有,则默认显示一个后退按钮,后退按钮的标题是A视图的标题。

    leftBarButtonItem会屏蔽掉系统的侧滑返回手势,而使用backBarButtonItem会保留侧滑手势。
    可以在baseViewController里把leftBarButtonItem和backBarButtonItem的创建和事件都写好,需要的时候就调用。如果需要拦截返回事件,就使用leftBarButtonItem,缺点是失去了侧滑返回效果。即使打开侧滑手势,也拦截不了返回方法。

    1. backBarButtonItem

    返回 文字

    特点:
    1.使用了自定义的返回箭头图片
    2.有返回文字
    3.保存了iOS 7以上系统的pop手势返回。

    
    - (void)setCustomBackBarItem{
          //替换系统的“<"图标
        [self.navigationController.navigationBar setBackIndicatorImage:[UIImage imageNamed:@"fan_hui.png"]];
        [self.navigationController.navigationBar setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"fan_hui.png"]];
    
            UIButton *btn = [[UIButton alloc]init];
            btn.frame =CGRectMake(0, 0, 57, 44);//frame 不显示
            [btn setImage:[UIImage imageNamed:@"fan_hui.png"] forState:(UIControlStateNormal)];//不起作用
            [btn addTarget:self action:@selector(backBarButtonItemReturnAction) forControlEvents:(UIControlEventTouchUpInside)];//不起作用
            UIBarButtonItem *leftItem0 = [[UIBarButtonItem alloc]initWithCustomView:btn];
            self.navigationItem.backBarButtonItem = leftItem0;
            self.navigationItem.backBarButtonItem.title = @"返回";
    //设置leftItem0之后,<之后显示的不是上一级的title,而是返回
    //效果等同于
    //UIBarButtonItem *leftItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStyleBordered target:nil action:nil];
    //self.navigationItem.backBarButtonItem = leftItem;
    
            self.navigationController.navigationBar.tintColor = TITLE;
    
    }
    
    - (void)backBarButtonItemReturnAction{
        [self.navigationController popViewControllerAnimated:YES];
    }
    
    

    不要 返回 文字

    特点:
    1.使用了自定义的返回箭头图片
    2.没有返回文字
    3.保存了iOS 7以上系统的pop手势返回。

    [self.navigationController.navigationBar setBackIndicatorImage:[UIImage imageNamed:@"navibar_back_btn_bg_normal.png"]];
    [self.navigationController.navigationBar setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"navibar_back_btn_bg_normal.png"]];
    
    UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStyleBordered target:nil action:nil];
    self.navigationItem.backBarButtonItem = backItem;
    

    设置自定义的返回箭头

    根据文档:
    1、只有这两个属性都设置了,才会显示自定义的图片。
    2、这两个属性,只能用于iOS 7以上。
    3、很多人问为什么要设置“backIndicatorTransitionMaskImage”这个属性,看官方文档就知道,当push、pop时显示的是这张image,但是疑问的是,当我设置这张照片跟“backIndicatorImage”属性的照片不一样时,系统不管是push,还是pop,还是完成时,都只会显示"backIndicatorImage"属性设置的照片。

    2. leftBarButtonItem

    一、自定义leftBarButtonItem

    第一种,自定义导航栏的返回按钮,虽然这看起来是一种方式,但是也不能从根本上解决;比如整个应用的返回键都是统一的,这时候再重写了某个界面的返回按钮感觉就不统一了。而且每有一个界面有这个需求都需要重新自定义一个返回按钮,显得不优雅。

    backBarButtonItem不能用View视图方法自定义,而leftBarButtonItem可以哦!
    加上 self.navigationController.interactivePopGestureRecognizer.delegate = self;才有手势返回哦!
    方案一:在B中开启侧滑手势

    - (void)viewWillAppear:(BOOL)animated{
       [super viewWillAppear:animated];
       if([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
           self.navigationController.interactivePopGestureRecognizer.delegate = (id)self;
       }
    }
    - (void)setCustomLeftButtonItem{
       UIButton *btn = [UIButton buttonWithType:(UIButtonTypeCustom)];
       btn.frame =CGRectMake(0, 0, 57, 44);
       [btn setTitle:@"返回" forState:(UIControlStateNormal)];
       [btn setTitleColor:TITLE forState:UIControlStateNormal];
       [btn setImage:[UIImage imageNamed:@"fan_hui.png"] forState:(UIControlStateNormal)];
       [btn addTarget:self action:@selector(leftBarButtonItemReturnAction) forControlEvents:(UIControlEventTouchUpInside)];
       UIBarButtonItem *leftItem0 = [[UIBarButtonItem alloc]initWithCustomView:btn];
       self.navigationItem.leftBarButtonItem = leftItem0;
       self.navigationController.navigationBar.tintColor = TITLE;
    }
    
    - (void)leftBarButtonItemReturnAction{
       [self.navigationController popViewControllerAnimated:YES];
    }
    

    方案二:在A中设置

    NextViewController *GoodsDetailVC = [[UIStoryboard storyboardWithName:@"Main" bundle:nil]instantiateViewControllerWithIdentifier:@"NextViewController"];
       //开启iOS7的滑动返回效果 能实现手势返回,但是监测不到点击返回事件
       if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
           self.navigationController.interactivePopGestureRecognizer.delegate = nil;
       }
       [self.navigationController pushViewController:GoodsDetailVC animated:YES];
    

    二、为 UINavigationController 添加 category

    此方法来自 github:UIViewController-BackButtonHandler

    由于系统的 UINavigationController 使用了一个 UINavigationBar 来管理 Controller 的 pop 和 push 等操作,所以仔细查看 UINavigationBar 的 API,会发现一个 UINavigationBarDelegate,它包含了四个方法:

    - (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPushItem:(UINavigationItem *)item; // called to push. return NO not to.
    - (void)navigationBar:(UINavigationBar *)navigationBar didPushItem:(UINavigationItem *)item;    // called at end of animation of push or immediately if not animated
    - (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;  // same as push methods
    - (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item;
    
    

    我们会惊喜的第一个不就是我们想要的效果吗?因为该方法返回 YES 则 pop,若返回NO,则不POP。

    因此我们可以为 UINavigatonController 创建一个 Category,来定制navigationBar: shouldPopItem: 的逻辑。这里需要注意的是,我们不需要去设置 delegate,因为 UINavigatonController 自带的 UINavigationBar 的 delegate 就是导航栏本身。这样还有个问题就是,那在实际的 Controller 里面怎么控制呢?因此同样需要对 UIViewController 添加一个 Protocol,这样在 Controller 中使用该 Protocol 提供的方法即可进行控制了,代码如下:

    // UIViewController+BackButtonHandler.h
    @protocol BackButtonHandlerProtocol <NSObject>
    @optional
    // 重写下面的方法以拦截导航栏返回按钮点击事件,返回 YES 则 pop,NO 则不 pop
    -(BOOL)navigationShouldPopOnBackButton;
    @end
    
    @interface UIViewController (BackButtonHandler) <BackButtonHandlerProtocol>
    
    @end
    
    
    // UIViewController+BackButtonHandler.m 
    @implementation UIViewController (BackButtonHandler)
    
    @end
    
    @implementation UINavigationController (ShouldPopOnBackButton)
    
    - (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
    
        if([self.viewControllers count] < [navigationBar.items count]) {
            return YES;
        }
    
        BOOL shouldPop = YES;
        UIViewController* vc = [self topViewController];
        if([vc respondsToSelector:@selector(navigationShouldPopOnBackButton)]) {
            shouldPop = [vc navigationShouldPopOnBackButton];
        }
    
        if(shouldPop) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [self popViewControllerAnimated:YES];
            });
        } else {
            // 取消 pop 后,复原返回按钮的状态
            for(UIView *subview in [navigationBar subviews]) {
                if(0. < subview.alpha && subview.alpha < 1.) {
                    [UIView animateWithDuration:.25 animations:^{
                        subview.alpha = 1.;
                    }];
                }
            }
        }
        return NO;
    }
    
    

    到这儿,几乎就完美的解决一开始的问题了,使用方法也非常简单

    (1). 在一个 Controller 中:

    #import "UIViewController+BackButtonHandler.h"
    
    

    (2). 重写 navigationShouldPopOnBackButton 方法

    - (BOOL)navigationShouldPopOnBackButton{
        [[[UIAlertView alloc] initWithTitle:@"提示" message:@"确定返回上一界面?"
    delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil] show];
        return NO;
    }
    // UIAlertViewDelegate
    - (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex{
        if (buttonIndex==1) {
            [self.navigationController popViewControllerAnimated:YES];
        }
    }
    
    

    总结:

    这两种方法点击返回的时候,都会触发拦截事件。但是手势返回,是不会触发的。手势返回的时候,监测不到leftBarButtonItemReturnAction和navigationShouldPopOnBackButton方法。

    3.侧滑手势

    以上的方法,都是基于系统自带的UINavigationController来自定义

    //官方文档:
    NS_CLASS_AVAILABLE_IOS(2_0) @interface UINavigationController : UIViewController
    @property(nullable, nonatomic, weak) id<UINavigationControllerDelegate> delegate;
    @property(nullable, nonatomic, readonly) UIGestureRecognizer *interactivePopGestureRecognizer NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED;
    

    相关文章

      网友评论

          本文标题:iOS自定义leftBarButtonItem、backBarB

          本文链接:https://www.haomeiwen.com/subject/qnahcqtx.html