美文网首页iOS技术点自己的swiftiOS 砖家纪实录
分享我所知道的实现navigationBar渐变的几种方法

分享我所知道的实现navigationBar渐变的几种方法

作者: ZeroJ | 来源:发表于2016-06-02 21:35 被阅读5887次

    一直有用到实现navigationBar渐变的效果, 这里就分享一下几种大家可能会尝试的不可行的方法和几种可行的方法Demo地址


    最终效果.gif

    首先我们来看看一些navigationBar的属性

    //这个属性设置是否半透明效果, 如果设置为NO, 则内容可能会向下偏移64
        public var translucent: Bool // Default is NO on iOS 6 and earlier. Always YES if barStyle is set to UIBarStyleBlackTranslucent
    // 这个属性是设置navigationBar上面的barButtonItem上的文字颜色
        public var tintColor: UIColor!
    // 这个属性是设置navigationBar的"背景色"
        public var barTintColor: UIColor? // default is nil
    // 这个方法设置背景图片
        public func setBackgroundImage(backgroundImage: UIImage?, forBarPosition barPosition: UIBarPosition, barMetrics: UIBarMetrics)
    // 这个属性设置阴影图片
        public var shadowImage: UIImage?
    
    

    需要注意的是当上面的属性设置不同值的时候navigationBar的层级结构是不一样的, 这个会在后面的方法中提到 比如:

    • 设置translucent==false的时候, navigationBar视图层级是这样的, 设置的barTintColor居然跑到了后面的红色view上面来了


      translucent
    • 设置translucent==true的时候, navigationBar视图层级是这样的, 设置的barTintColor在navigationBar的层级上显示


      translucent.png

    首先我们监控页面中的scrollView(tableView, collectionView...)的滚动, 在里面动态的设置alpha值, 我这里的设置如下

    extension ViewController: UIScrollViewDelegate {
        func scrollViewDidScroll(scrollView: UIScrollView) {
            let offsetY = scrollView.contentOffset.y
            // 这里面改变的程度和什么时候改变, 你可以根据自己的项目需要自己设置
            if offsetY <= 0 {
                let alpha = offsetY / -64.0
                setNavAlpha(alpha)
            } else {
                setNavAlpha(0.0)
            }
        }
        // 这里面设置navigationBar的alpha
        func setNavAlpha(alpha: CGFloat) {
        }
    }
    
    

    1. 最容易想到的方法, 要实现渐变, 直接设置它的alpha应该就可以了

    • 首先在viewDidLoad()里设置navigationBar的"背景色"
            navigationController?.navigationBar.barTintColor = UIColor.redColor()
    

    设置barTintColor后navigationBar的层次是这样的, 可以看到里面有很多层的view还有UIImageView(其中有用来设置barTintColor, backgroudImage, 实现半透明的view...)

    barTintColor.png
    • 设置navigationBar.alpha
        func setNavAlpha(alpha: CGFloat) {
            navigationController?.navigationBar.alpha = alpha
        }
    

    这样处理的效果如下

    alpha.gif

    可以看到navigationBar确实能够渐变, 效果很好, 但是
    这个需要注意的是, 设置view的alpha的时候, 他也会有改变这个view上的所有的子控件的alpha的效果, 所以上面的title和左右的按钮都跟着一起渐变, 我希望的是只有navigationBar的"背景色"渐变, 其他的仍然显示在上面, 所以这种一定程度上不能满足要求

    2.动态改变barTintColor的alpha或者backgroundColor

        func setNavAlpha(alpha: CGFloat) {
            navigationController?.navigationBar.barTintColor = UIColor(red: 223.0/255.0, green: 223.0/255.0, blue: 223.0/255.0, alpha: alpha)
        }
    

    动态改变barTintColor的alpha是没有效果的, 具体的原因不清楚, 不过如果重新设置barTintColor 是有贵改变颜色的

    3. 设置背景图片, 动态改变背景图片的透明度来实现

    设置背景图片后的navigationBar的层级结构


    image.png

    可以看到已经少了两层了, 只剩三层

    • 通过一个颜色获得一张图片的方法如下, 涉及一点点绘图知识
        
        func drawImageFromColor(color: UIColor, size: CGSize) -> UIImage {
            UIGraphicsBeginImageContextWithOptions(size, false, 0)
            color.set()
            UIRectFill(CGRect(origin: CGPointZero, size: size))
            let image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return image
            
        }
    
    • viewDidLoad()中设置背景色
            //背景色
            let image = drawImageFromColor(UIColor.redColor().colorWithAlphaComponent(1.0), size: CGSize(width: view.bounds.width, height: 64.0))
            
            navigationController?.navigationBar.setBackgroundImage(image, forBarMetrics: .Default)
    
    • 动态改变背景图片
        func setNavAlpha(alpha: CGFloat) {
            let image = drawImageFromColor(UIColor.redColor().colorWithAlphaComponent(alpha), size: CGSize(width: view.bounds.width, height: 64.0))
            navigationController?.navigationBar.setBackgroundImage(image, forBarMetrics: .Default)
        }
    

    这样处理后的效果是这样的

    image.gif

    渐变的效果确实是实现了我们想要的了, 但是可以看到, 其中还有一条线是我们不想要的, 这里通过如下的设置可以去掉这条线, 然后效果就很好了

            navigationController?.navigationBar.shadowImage = UIImage()
    
    

    4. 直接修改navigationBar的第一层subview的alpha

    • viewDidLoad()中设置颜色
            navigationController?.navigationBar.setBackgroundImage(UIImage(), forBarMetrics: .Default)
            navigationController?.navigationBar.shadowImage = UIImage()
            navigationController?.navigationBar.barTintColor = UIColor.redColor()
    
    
    • 动态修改alpha
        func setNavAlpha(alpha: CGFloat) {
            navigationController?.navigationBar.subviews.first?.alpha = alpha
        }
    

    5. 添加一个自定义的view到navigationBar最底部, 动态改变这个view的alpha来实现

    • 在viewDidLoad()里面添加view
            navigationController?.navigationBar.setBackgroundImage(UIImage(), forBarMetrics: .Default)
            navigationController?.navigationBar.shadowImage = UIImage()
    //
            let test = UIView(frame: CGRect(x: 0.0, y: -20.0, width: UIScreen.mainScreen().bounds.size.width, height: 64.0))
            test.backgroundColor = UIColor.redColor()
            navigationController?.navigationBar.insertSubview(test, atIndex: 0)
    
    • 动态改变这个View的alpha, 因为navigationBar上的title等其他控件不在这个view上, 所以改变他的alpha不会影响其他的控件
        func setNavAlpha(alpha: CGFloat) {
            navigationController?.navigationBar.subviews.first?.alpha = alpha
        }
    

    这样设置后效果也是我们想要的了

    6. 在5.的基础上修改一下, 在UINavigationController分类中使用运行时添加一个属性alphaView, 然后添加一个方法来设置他的alpha

    • extension如下
    var alphaViewKey = "alphaViewKey"
    extension UINavigationController {
        var zj_alphaView: UIView? {
            set {
                objc_setAssociatedObject(self, &alphaViewKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            }
            
            get {
                return objc_getAssociatedObject(self, &alphaViewKey) as? UIView
            }
        }
        
        func zj_setBackgroundColor(color: UIColor, alpha: CGFloat) {
            
            if zj_alphaView == nil {
                zj_alphaView = UIView(frame: CGRect(x: 0.0, y: -20.0, width: UIScreen.mainScreen().bounds.width, height: 64.0))
                navigationBar.setBackgroundImage(UIImage(), forBarMetrics: .Default)
                navigationBar.shadowImage = UIImage()
            navigationBar.insertSubview(zj_alphaView!, atIndex: 0)
            }
            zj_alphaView?.backgroundColor = color.colorWithAlphaComponent(alpha)
            
            
        }
    }
    
    • 使用
        func setNavAlpha(alpha: CGFloat) {
            navigationController?.zj_setBackgroundColor(UIColor.redColor(), alpha: alpha)
        }
    
    7.隐藏或者将navigationBar直接设置为完全透明的, 然后自己添加一个view到顶部来当作navigationBar, 这种方式比较简单实现(不少人也是这儿干的), 但是很多系统的过渡动画就没有了, 大家可以自己试试

    注意, 以上各种有效的方法设置后一定要记得, 在退出当前Vc的时候将navigationBar的状态回复为原来的, 避免影响其他的界面的效果


    最后提供Demo地址,如果你觉得有帮助,不妨给个star, 也可以看看里面其他的东西, 欢迎关注


    相关文章

      网友评论

      • azhunchen:// 需要修改该属性,否则 navigationBarButton 点击无反应
        zj_alphaView?.isUserInteractionEnabled = false

        // 每次都把该 view 放到最下层,就无忧了
        navigationBar.sendSubview(toBack: zj_alphaView!)
        zj_alphaView?.backgroundColor = color.colorWithAlphaComponent(alpha)
      • 静花寒:有个问题询问下,实际开发中,viewcontroller会继承自baseviewcontroll,此时,导航栏透明就无效了,怎么解决这个bug?
        再来一份果子狸:@静花寒 那一层比较好玩,其实是navigationController?.navigationBar.isTranslucent带来的效果涂层,隐藏的方法就是设定她的透明度,如果把isTranslucent关掉之后,则有一个背景图层,设置方式是barTintColor而不是TintColor。当然,还有可能是backgroundcolor,细细分辨下
        静花寒:@ZeroJ 但我实际开启后,是无效的,看图层,是有一层是白色的,怎么去都去不掉的
        ZeroJ:@静花寒 朋友,导航栏是子控制器所共有的,修改一个地方也会影响其他地方,和是否继承没有关系,只需要在需要透明的控制器中设置开启即可
      • qggz0524:这样写还是有问题的,只要push的界面 的导航栏都会加了这个视图
        ZeroJ:@qggz0524 导航栏因为系统中是公用的,所以是会所有页面都会加载这个视图,但是,这并不重要,只需要在使用的地方开启,使用完毕了关闭就好,侵入性并不大
      • 槑头脑:这些是在 translucent==true 的基础上吧
      • 郑州程序员王一:楼主好,能写个OC的不。
      • 哈南:学习了 :pray:
        ZeroJ:@哈南 :smiley:
      • 杨小黑:正巧想看下这个的实现方法,感谢楼主~!:blush:
        ZeroJ:@杨小黑 :smile: 很高兴
      • jswift:太完整了, 多谢分享
      • STDawn:mark

      本文标题:分享我所知道的实现navigationBar渐变的几种方法

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