iOS开发------响应TableView下拉设置NavigationBar的透明度

来源:互联网 发布:好家伙 盗亦有道 知乎 编辑:程序博客网 时间:2024/06/07 16:27

       最近工作不忙的时候会偷懒玩一会手机,会发现各大App中的一些小细节很吸引人,这次看到一个很有意思的效果,其实这个效果应用的已经很普遍,就是响应scrollView的下滑,NavigationBar从无到有(其实就是透明度从0到1的过程),看起来总是简单的,实现起来总会遇到点麻烦,这次也一样,经过查阅相关技术博客以及花点时间研究原理,也慢慢的理解实现了出来,个人还是觉得了解原理要比直接拿来代码要好的多,大体效果如下:


这里先附上代码的GitHub:https://github.com/YRunIntoLove/YSinaNavigationBarDemo


                                                                   


直接设置NavigationBar的背景颜色透明度----fail

首先想到的必然是直接修改背景(background)的alpha(透明度属性),即在scrollViewDidScroll中根据比例设置响应的透明度即可
self.navigationController?.navigationBar.backgroundColor = UIColor.orangeColor().colorWithAlphaComponent(alpha)

如果这么设置了,那么大家就和楼主一样想的太过简单了,尝试过后发现根本没有任何的效果,透明度没有任何的变化


设置NavigationBar的alpha----fail

如果设置背景颜色不能完成,那么我直接设置NavigationBar的透明度不就好了嘛
self.navigationController?.navigationBar.alpha = 0.3

这样子做当然可以,但是会发现一个小小的问题,就是贴在NavigationBar上的Title以及相关响应按钮等相关组件也会根据父视图的透明度变透明了,这似乎也不是咱们理想中的效果

利用runtime的关联属性,为NavigationBar添加一层自定义的视图层----Success

       没有办法,打开百度一搜,果然会发现有很多这样子的尝试,最终还是需要动用runtime来自定义添加一个视图最为合适,动态关联属性算是runtime中很简单的知识了,花点时间了解一下也就会理解了,也可以在前面的博客 iOS开发----runtime关联对象(动态添加属性)中稍微的回顾一下。
     
首先需要新建一个NavigationBar的类目(拓展),因为最近学习Swift,代替Objective-C中类目的是Swift中的extension,需要在拓展中添加一个属性,说是属性,实际上是添加了一个get和set方法,自定义一个视图
var key:String = "CoverView"extension UINavigationBar{        var coverView:UIView?{                set{            //runtime添加动态关联的属性            objc_setAssociatedObject(self, &key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)        }                get{            //runtime读取动态关联的属性            return objc_getAssociatedObject(self, &key) as? UIView        }    }

然后就对这个动态关联的视图进行颜色的设置
/** *  设置 背景色 */@available(iOS 8.0,*)func setViewColor(color:UIColor){    //如果覆盖图层为nil    if(self.coverView == nil)    {        //设置背景图片及度量        self.setBackgroundImage(UIImage(), forBarMetrics: .Default)        //去除自定义背景图后形成的下端黑色横线        self.shadowImage = UIImage()        //设置图层的frame        let view = UIView(frame: CGRect(x: 0, y: -20, width: UIScreen.mainScreen().bounds.width, height: CGRectGetHeight(self.frame) + 20))        view.userInteractionEnabled = false//人机不交互        view.autoresizingMask = [.FlexibleWidth , .FlexibleHeight]//自适应宽度和高度        //将图层添加到导航Bar的底层        self.insertSubview(view, atIndex: 0)                //因为这里不是一个真正的属性,是在runtime时进行关联的属性,所以相关属性的修改需要实例对象来"赋值"        self.coverView = view    }        self.coverView?.backgroundColor = color}

这里说一下,其实Xcode提供给大家一个查看UI层次的功能<#楼主才知道,- - 感觉对Xcode的了解太差劲了#> Debug -> View Debuging -> Capture View Hierarchy
它可以让我们看到不少组件中私有属性,在这里可以看到NavigationBar的子视图中有这个一个子视图(_ UINavigationBarBackground),他的子视图中有一个UIImageView,也就是当我们设置背景图片或者背景色的时候,应该就是这个视图在响应。




当然设置透明度用上面的方法是可以的,在颜色中添加透明度即可,但是习惯性的还是写了一个接口
/** *  设置透明度 */@available (iOS 8.0, *)func setViewAlpha(alpha:CGFloat){    //如果view = self.coverView不成立,就return    guard let view = self.coverView else    {        return    }        self.coverView!.backgroundColor = view.backgroundColor?.colorWithAlphaComponent(alpha)}

上面的效果因为push之后出现的NavigationBar的颜色不一样,所需的效果也不一样,所以为了避免影响之后NavigationBar的效果,需要写一个移除该图层的方法,如下
/** *  清除图层,视图消失时需要调用该方法,不然会影响其他页面的效果 */@available (iOS 8.0, *)func relieveCover(){    self.setBackgroundImage(nil, forBarMetrics: .Default)    coverView?.removeFromSuperview()    coverView = nil}


当然上面的已经完全可以实现上面的功能了,但想要功能更加圆滑一点,便又封装了一个CustomHeaderView

首先需要定义几个相关的属性
class CustomHeaderView: UIView {        /// 代理    weak var delegate:CustomHeaderViewDelegate?    ///底层控制ImageView缩放的View,后面通过更改它的frame属性来实现圆滑效果    var contentView:UIView! = UIView()        /// 存放外部传入的视图,即ImageView    var subView:UIView        /// 最大的下拉距离    var maxContentOff:CGFloat        /// 起点的纵坐标    private let originY:CGFloat = -64

接着实现自定义构造方法
init(subView:UIView,maxContentOff:CGFloat,headerViewSize: CGSize,delegate: CustomHeaderViewDelegate){    self.subView = subView//当前的imageView    self.delegate = delegate    self.maxContentOff = maxContentOff > 0 ? -maxContentOff : maxContentOff//因为向下滑动是负数,进行数字正负转换        super.init(frame: CGRectMake(0, 0, headerViewSize.width, headerViewSize.height))        //开始自动布局设置,意思是自动将subView的frame与superView相一致    subView.autoresizingMask = [.FlexibleTopMargin,.FlexibleBottomMargin,.FlexibleLeftMargin,.FlexibleRightMargin,.FlexibleWidth,.FlexibleHeight]        //此视图不显示越界的视图    self.clipsToBounds = false    self.contentView.frame = self.bounds    self.contentView.addSubview(subView)        //存放ImageView的视图需要显示越界的视图    self.contentView.clipsToBounds = true    self.addSubview(contentView)}required init?(coder aDecoder: NSCoder) {    fatalError("init(coder:) has not been implemented")}


因为涉及到往外传值,所以必须使用回调,这里使用的是Delegate(委托)回调,当然是可以使用闭包传值的,定义协议如下:
protocol CustomHeaderViewDelegate : class{        /**     滚动已经到达最大偏移量,需要锁定滚动视图          :param: customHeaderView     :param: maxContentOffSet 最大偏移量     */    @available(iOS 8.0,*)    func customHeaderView(customHeaderView:CustomHeaderView,lockScrollView  maxContentOffSet:CGFloat)                /**     滚动过程中修改导航Bar的透明度          :param: customHeaderView     :param: alpha            透明度     */    @available(iOS 8.0,*)    func customHeaderView(customHeaderView:CustomHeaderView,shouldChangeBarAlpha alpha:CGFloat)}

最后就是需要一个对外开放的方法,设置之前说的相关contentView的frame实现圆滑效果以及触发Delegate方法
// MARK: - 对外接口func layoutHeaderWillScroll(offSet:CGPoint){    //获取垂直偏移量    let contentOffY = offSet.y        //如果偏移量大于最大偏移量,因为是负数,所以是小于    if(contentOffY < maxContentOff)    {        //锁定坐标        self.delegate?.customHeaderView(self, lockScrollView: maxContentOff)    }        else if(contentOffY < 0)//如果小于0,表示headerView还显示在ScrollView中    {        var rect = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)                rect.origin.y += contentOffY ;        rect.size.height -= contentOffY;        self.contentView.frame = rect;    }        //64 + 当前的垂直偏移量    let alpha = (-originY + contentOffY) / self.frame.size.height        //设置透明度    self.delegate?.customHeaderView(self, shouldChangeBarAlpha: alpha)}


在主ViewController中使用即可

定义初始化的属性以及在ViewDidLoad中进行初始化
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,CustomHeaderViewDelegate{    var tableView: UITableView!    var imageView: UIImageView!        var imageHeight:CGFloat?    var imageDistance:CGFloat?        let barColor = UIColor.orangeColor()        override func viewDidLoad() {                super.viewDidLoad()        //设置导航栏的属性        self.navigationController?.navigationBar.setViewColor(barColor.colorWithAlphaComponent(0.0))                //设置列表属性        tableView = UITableView(frame: self.view.bounds)        tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")        tableView.dataSource = self        self.view.addSubview(tableView)                        //设置显示图片的视图        imageView = UIImageView(frame: CGRectMake(0, 0, self.view.bounds.size.width, 100))        imageView.contentMode = .ScaleAspectFill        imageView.image = UIImage(named: "backGround.jpg")                let customHeaderView = CustomHeaderView(subView: imageView, maxContentOff: -120, headerViewSize: CGSize(width: self.view.bounds.size.width,height: 100),delegate:self)        tableView.tableHeaderView = customHeaderView    }

为了不影响其他的控制器导航栏,不要忘记在消失的时候取消ScrollView的代理以及对NavigationBar图层的去除,
override func viewWillAppear(animated: Bool){    super.viewWillAppear(animated)    tableView.delegate = self;}override func viewWillDisappear(animated: Bool){    tableView.delegate = nil    super.viewWillDisappear(animated)    self.navigationController?.navigationBar.relieveCover()}

在tableView中实现ScrollViewDidScroll:协议方法,调用headerView的对外接口即可
//MARK: - UIScrollView Delegatefunc scrollViewDidScroll(scrollView: UIScrollView){        //获得当前的自定义HeaderView对象    let customView:CustomHeaderView = (scrollView as! UITableView).tableHeaderView as! CustomHeaderView        //设置滚动    customView.layoutHeaderWillScroll(scrollView.contentOffset)    }

实现CustomHeaderView的代理方法
//MARK: - CustomHeaderViewDelegatefunc customHeaderView(customHeaderView: CustomHeaderView, lockScrollView maxContentOffSet: CGFloat) {       //锁定滚动视图    self.tableView.contentOffset.y = maxContentOffSet}func customHeaderView(customHeaderView: CustomHeaderView, shouldChangeBarAlpha alpha:CGFloat) {      //设置透明度    self.navigationController?.navigationBar.setViewColor(self.barColor.colorWithAlphaComponent(alpha))}
0 0
原创粉丝点击