最近工作不忙的时候会偷懒玩一会手机,会发现各大App中的一些小细节很吸引人,这次看到一个很有意思的效果,其实这个效果应用的已经很普遍,就是响应scrollView的下滑,NavigationBar从无到有(其实就是透明度从0到1的过程),看起来总是简单的,实现起来总会遇到点麻烦,这次也一样,经过查阅相关技术博客以及花点时间研究原理,也慢慢的理解实现了出来,个人还是觉得了解原理要比直接拿来代码要好的多,大体效果如下:
这里先附上代码的GitHub:https://github.com/YRunIntoLove/YSinaNavigationBarDemo
self.navigationController?.navigationBar.backgroundColor = UIColor.orangeColor().colorWithAlphaComponent(alpha)
self.navigationController?.navigationBar.alpha = 0.3
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
}
/**
* 设置透明度
*/
@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)
}
/**
* 清除图层,视图消失时需要调用该方法,不然会影响其他页面的效果
*/
@available (iOS 8.0, *)
func relieveCover()
{
self.setBackgroundImage(nil, forBarMetrics: .Default)
coverView?.removeFromSuperview()
coverView = nil
}
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")
}
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)
}
// 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)
}
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
}
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()
}
//MARK: - UIScrollView Delegate
func scrollViewDidScroll(scrollView: UIScrollView){
//获得当前的自定义HeaderView对象
let customView:CustomHeaderView = (scrollView as! UITableView).tableHeaderView as! CustomHeaderView
//设置滚动
customView.layoutHeaderWillScroll(scrollView.contentOffset)
}
//MARK: - CustomHeaderViewDelegate
func 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))
}