iOS中 UINavigation 导航栏,导航条 的深度定制

本文解决的问题

  1. 如何深度定制 NavigationBar 样式
  2. 统一设置APP的返回按钮
  3. 解决自定义leftItem后手势失效的问题

1. 自定义NavigationBar


有些APP的导航条定制程度很高,这边抛砖引玉说一下自己用过的一个简单的办法.

当你直接更改NavigationController的NavigationBar,会报一个错误.这个属性是get-only

! Cannot assign to property: 'navigationBar' is a get-only property
navigationBar = UINavigationBar()

可能你是用KVC赋值能够更改NavigationBar,但是我要说的思路也可以作为一个参考.
我们先看一下功能需求点,以及实现的效果

  1. 返回按钮图片自定义
  2. 返回按钮在侧滑手势激活的时候,跟随划出的控制器一起移动.(系统自带的backItem会留在原地渐渐变淡.)
  3. navBar中的样式随着ScrollView.contentOffset.y动态改变.
自定义NavBar

1.1自定义样式.

  1. 设置自带的NavBar为全透明
  2. 创建一个和NavBar一样大小的view添加到NavBar的下面
    //实例化navBarView
    ZFHomeShopNavigationBarView *navBar = [[ZFHomeShopNavigationBarView alloc]init];
    [self.view insertSubview:navBar aboveSubview:self.navigationController.navigationBar];

    //设置约束
    [navBar mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.leading.trailing.equalTo(self.view);
        make.height.mas_equalTo(64);
    }];

1.2 返回按钮在侧滑手势激活的时候,跟随界面一起移动.

其实这个效果在第一步就实现了. 我创建的navBar对象实际上是一个view,返回按钮是固定在view内部的约束. 所以当我们将navBar的约束的leading约束到控制器的view上,控制器开始转场我们的navBar自然跟随着控制器移动.

1.3 动画改变样式.

这个案例中,navBar的背景色是从全透明慢慢的转为不透明的.

  1. 重写了setAlpha:方法,在方法内改变navBar的内部空间.
  2. 其他的控件的颜色由白->黑
  3. 需要创建两套一模一样的ImageView,他们本身的素材颜色就是黑白两种颜色. 如果只有一套过渡效果在由黑变白的时候会不自然.如果自己需要实现可以先试一下一套
  4. 通过alpha,正反比设置两套控件的透明度
- (void)setAlpha:(CGFloat)alpha
{
    //背景颜色成正比.
    self.backgroundColor = [UIColor colorWithWhite:1 alpha:alpha];

    //黑色控件透明度成正比
    self.iv_ShareBlack.alpha = alpha;
    self.iv_SearchBlack.alpha = alpha;
    self.iv_BackBlack.alpha = alpha;
    self.lblTitle.alpha = alpha;
    self.blackLine.alpha = alpha;
    
    //白色成反比
    CGFloat alpha1 = 1 - alpha;
    self.iv_BackWhite.alpha = alpha1;
    self.iv_ShareWhite.alpha = alpha1;
    self.iv_SearchWhite.alpha = alpha1;
}

1.4 需要增加两个item来监听点击事件

刚才在navBar这个view里面创建的都是ImageView.所以我们还需要在Nav上创建两个真实的item来接收点击事件. 在navBar里面的button接收不到点击

//创建2个item监听点击
    self.navigationItem.rightBarButtonItems = @[[[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:@"navItemBackground"] style:UIBarButtonItemStyleDone target:self action:@selector(clickShare)],[[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:@"navItemBackground"] style:UIBarButtonItemStyleDone target:self action:@selector(clickSearch)]];
iOS中 UINavigation 导航栏,导航条 的深度定制_第1张图片
中间的竖条是我设置的透明图片,进行占位.使得按钮大小和图片大小一致.按钮需要黑白各一套素材.实现平滑的过渡

那么以上就是第一点,实现navBar的单个界面深度定制.这个办法比较傻比较简单.而且通用性强.不会影响到自带的手势,是一个不错的解决方案..

2. 统一设置APP的返回按钮


2.1 隐藏返回按钮文字

    //设置导航条文字
    self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];

这行代码可以写在BaseViewController中.也就是抽取的VC的基类.或者重写NavigationController.在push方法之前,对VC进行设置.不过需要判断栈内是否有VC,通常栈底的rootVC是不进行设置的.

2.2 更改navBar背景色,隐藏尴尬的黑线

    //设置背景图片
    [self.navigationBar setBackgroundImage:[UIImage imageNamed:@"nav"] forBarMetrics:UIBarMetricsDefault];
    
    //设置阴影图片
    [self.navigationBar setShadowImage:[UIImage new]];
  1. 背景色可以使用图片素材来更改
  2. 阴影是navBar下面会有一条很细的黑线,如果是白色的背景会看的比较明显.
  3. 可以写在Nav的基类中,或者在UINavigationController分类中重写loadView方法,貌似也可以.

2.3 统一设置返回按钮样式

    //获取navBar的全局代理对象
    UINavigationBar *allNavBar = [UINavigationBar appearance];
    //返回按钮的图片
    UIImage *backImage = [[UIImage imageNamed:@"home_nav_back"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    //设置返回按钮样式
    [allNavBar setBackIndicatorImage:backImage];
    [allNavBar setBackIndicatorTransitionMaskImage:backImage];

这句代码可以写在AppDelegate中,在设置完window的rootVC,在经典架构下rootVC通常是tabBar,tabBar的每一页通常是NavigationController.此时获取全局代理对象,进行统一的设置,以后都不需要管理了.

3. 解决自定义leftItem后手势失效的问题


以下为Swift代码,OC代码参考文章

3.1 继承NavigationController.

使用自己的Nav,并遵守两个协议

import UIKit

class ZFNavigationController: UINavigationController,UIGestureRecognizerDelegate

3.2 可以在push方法中进行按钮的统一设置

 /// 重写push方法,每次push都为下一个控制器修改统一的返回按钮以及手势.
    override func pushViewController(_ viewController: UIViewController, animated: Bool) {
        //判断当前的栈内有几个视图.为0的话是根控制器,根控制器不需要设置返回按钮以及返回手势
        if self.childViewControllers.count > 0  {
            //设置返回按钮,这里使用的分类中自定义的创建方法.
            viewController.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "返回" , image: #imageLiteral(resourceName: "navigationbar_back_withtext"), highlightedImage: #imageLiteral(resourceName: "navigationbar_back_withtext_highlighted"), target: self, action: #selector(back))
            }        
        //调用父类方法
        super.pushViewController(viewController, animated: animated)
        /// 结束重写
    }

3.3 解决手势问题.

    /// 重写push方法,每次push都为下一个控制器修改统一的返回按钮以及手势.
    override func pushViewController(_ viewController: UIViewController, animated: Bool) {
        //判断当前的栈内有几个视图.为0的话是根控制器,根控制器不需要设置返回按钮以及返回手势
        if self.childViewControllers.count > 0  {
            //设置返回按钮,这里使用的分类中自定义的创建方法.分类在3.4
            viewController.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "返回" , image: #imageLiteral(resourceName: "navigationbar_back_withtext"), highlightedImage: #imageLiteral(resourceName: "navigationbar_back_withtext_highlighted"), target: self, action: #selector(back))

            //获取返回手势的代理
            let target = interactivePopGestureRecognizer?.delegate
            
            //自定义手势添加到返回手势的代理上
            let pan = UIScreenEdgePanGestureRecognizer(target: target, action: NSSelectorFromString("handleNavigationTransition:"))
            pan.edges = UIRectEdge.left
            
            //如果需要使用全屏侧滑返回,可以直接使用pan手势,并且很奇妙的是也只能从左往右滑动生效
            //let pan =  UIPanGestureRecognizer(target: target, action: NSSelectorFromString("handleNavigationTransition:"))
            //将自定义手势添加到控制器的View上
            viewController.view.addGestureRecognizer(pan)
            
            //设置代理,控制手势是否开启,以及滑动的方向距离等.若不需要可以不设置
            pan.delegate = self
            
            // 设置返回手势
            interactivePopGestureRecognizer?.isEnabled = true
        }
        
        //调用父类方法
        super.pushViewController(viewController, animated: animated)
        
        /// 结束重写
    }

    /// 返回按钮绑定的点击事件
    @objc private func back() {
        popViewController(animated: true)
    }
    
    /// 手势的代理,开启手势识别.弱不需要额外的控制可以不设置
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }

3.4 创建barItem的分类


import UIKit

extension UIBarButtonItem {
    convenience init(title : String = "" , image : UIImage? , highlightedImage : UIImage? , target: Any?, action: Selector) {
        let barButton = UIButton()
        //设置title
        barButton.setTitle(title, for: .normal)
        barButton.setTitleColor(UIColor.init(red: 0.24, green: 0.24, blue: 0.24, alpha: 1), for: .normal)
        barButton.setTitleColor(UIColor.init(red: 1, green: 0.46, blue: 0, alpha: 1), for: .highlighted)
        barButton.titleLabel?.font = UIFont.systemFont(ofSize: 14)
        
        //设置图片
        if let image = image {
        barButton.setImage(image, for: .normal)
        }
        
        //高亮图片
        if let highlightedImage = highlightedImage {
        barButton.setImage(highlightedImage, for: .highlighted)
        }
        
        //添加target
        barButton.addTarget(target, action: action, for: .touchUpInside)
        
        //尺寸适应
        barButton.sizeToFit()
        
        //构造.
        self.init()
        
        //添加到CustomView
        customView = barButton
    }
}

你可能感兴趣的:(iOS中 UINavigation 导航栏,导航条 的深度定制)