来自Leo的原创博客,转载请著名出处
我的StackOverflow
我的Github
https://github.com/LeoMobileDeveloper
淘宝
大众点评
其中
完整的代码地址:PullToRefreshKit
Tips:用Swift 2.2写的,所以需要XCode 7.3来运行。
这是我用纯Swift 2.2写的一个库,初衷是为了更简单的实现自定义下拉刷新。当然,它也支持一代码实现:上拉加载,左/右滑动加载更多的操作。
比如,用一行代码实现默认的下拉刷新
self.tableView.setUpHeaderRefresh { [weak self] in
delay(1.5, closure: {
self?.tableView.endHeaderRefreshing(.Success)
})
}
效果
不过,这个库的主要目的还是希望大家能方便的实现自定义刷新界面,不管是哪个方向的。
通过它来自定义刷新界面,只需要实现三个协议中的一个。
比如,实现自定义下拉刷新,只需写一个UIView的子类,要遵循协议RefreshableHeader。这个UIView的子类,你可以用AutoLayout,任何你想要使用的布局效果。
这个协议有如下几个方法
//拖拽的触发刷新的距离,也是Header的高度
func distanceToRefresh()->CGFloat
//拖拽或者释放的时候,比例的变化。比如总高度是100,当拖拽为10的时候,比例就是0.1,在这里,可以根据百分比动态的设置Header的状态
func percentUpdateWhenNotRefreshing(percent:CGFloat)
//松手即将进入刷新状态的回调,在这里,把视图切换为动画状态
func releaseWithRefreshingState()
//刷新结束,将要开始隐藏Header的动画,在这里告诉用户刷新失败或者成功
func didBeginEndRefershingAnimation(result:RefreshResult)
//刷新结束,Header完全隐藏的回调,这里把Header恢复到最初的状态
func didCompleteEndRefershingAnimation(result:RefreshResult)
分析一下,大众点评的下拉刷新主要分为两个状态
首先,我们准备好图片,本文的图片来自于MJ哥的MJRefresh
其中,下拉的过程的图片,一共有60张,刷新的时候,动图有3张。
这60张图是不一样的,比如第一张,第1,30,60张如下,下拉的过程就是不断的切换图片
至于刷新的过程中,就是用Imageview,动态播放以下三张图片罢了
所以,代码如下
class DianpingRefreshHeader:UIView,RefreshableHeader{
let imageView = UIImageView()
//放置Imageview
override init(frame: CGRect) {
super.init(frame: frame)
imageView.frame = CGRectMake(0, 0, 60, 60)
imageView.center = CGPointMake(CGRectGetWidth(self.bounds)/2.0, CGRectGetHeight(self.bounds)/2.0 + 10)
addSubview(imageView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - RefreshableHeader -
//一共的距离是70
func distanceToRefresh()->CGFloat{
return 60
}
//监听百分比变化,切换图片
func percentUpdateWhenNotRefreshing(percent:CGFloat){
imageView.hidden = (percent == 0)
let adjustPercent = max(min(1.0, percent),0.0)
let scale = 0.2 + (1.0 - 0.2) * adjustPercent;
imageView.transform = CGAffineTransformMakeScale(scale, scale)
let mappedIndex = Int(adjustPercent * 60)
let imageName = "dropdown_anim__000\(mappedIndex)"
let image = UIImage(named: imageName)
imageView.image = image
}
//松手即将刷新,播放动图
func releaseWithRefreshingState(){
let images = ["dropdown_loading_01","dropdown_loading_02","dropdown_loading_03"].map { (name) -> UIImage in
return UIImage(named:name)!
}
imageView.animationImages = images
imageView.animationDuration = 0.6
imageView.startAnimating()
}
//刷新结束,将要隐藏header,不做任何处理
func didBeginEndRefershingAnimation(result:RefreshResult){
}
//刷新结束,完全隐藏header,恢复到最初状态
func didCompleteEndRefershingAnimation(result:RefreshResult){
imageView.animationImages = nil
imageView.stopAnimating()
imageView.hidden = true
}
}
然后,这样调用
let dianpingHeader = DianpingRefreshHeader(frame: CGRectMake(0,0,CGRectGetWidth(self.view.bounds),60))
self.tableView.setUpHeaderRefresh(taobaoHeader) { [weak self] in
delay(1.5, closure: {
self?.tableView.endHeaderRefreshing(.Success)
})
}
首先分析视图架构
整个下拉刷新的界面如下
其中
所以,完整的代码如下
class TaoBaoRefreshHeader:UIView,RefreshableHeader{
private let circleLayer = CAShapeLayer()
private let arrowLayer = CAShapeLayer()
private let textLabel = UILabel()
private let strokeColor = UIColor(red: 135.0/255.0, green: 136.0/255.0, blue: 137.0/255.0, alpha: 1.0)
override init(frame: CGRect) {
super.init(frame: frame)
setUpCircleLayer()
setUpArrowLayer()
textLabel.frame = CGRectMake(CGRectGetWidth(self.bounds)/2 - 30, CGRectGetHeight(self.bounds)/2 - 20,120, 40)
textLabel.textAlignment = .Center
textLabel.textColor = UIColor.lightGrayColor()
textLabel.font = UIFont.systemFontOfSize(14)
textLabel.text = "下拉即可刷新..."
self.addSubview(textLabel)
let imageView = UIImageView(frame: CGRectMake(0, 0, 230, 35))
imageView.image = UIImage(named: "taobaoLogo")
self.addSubview(imageView)
self.addSubview(textLabel)
}
//绘制中间箭头
func setUpArrowLayer(){
//略去
}
//绘制外部圆圈
func setUpCircleLayer(){
//略去
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - RefreshableHeader -
func distanceToRefresh()->CGFloat{
return 60
}
//根据滑动百分比,动态调整storkeEnd和文字
func percentUpdateWhenNotRefreshing(percent:CGFloat){
let adjustPercent = max(min(1.0, percent),0.0)
self.circleLayer.strokeEnd = 0.05 + (0.95 - 0.05) * adjustPercent
if adjustPercent == 1.0{
textLabel.text = "释放即可刷新..."
}else{
textLabel.text = "下拉即可刷新..."
}
}
//进入刷新状态,调整圈圈的strokeEnd,为圈圈增加旋转动画,更新label文字
func releaseWithRefreshingState(){
self.circleLayer.strokeEnd = 0.95
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotateAnimation.toValue = NSNumber(double: M_PI * 2.0)
rotateAnimation.duration = 0.6
rotateAnimation.cumulative = true
rotateAnimation.repeatCount = 10000000
self.circleLayer.addAnimation(rotateAnimation, forKey: "rotate")
self.arrowLayer.hidden = true
textLabel.text = "刷新中..."
}
//结束刷新的动画开始,停止动画
func didBeginEndRefershingAnimation(result:RefreshResult){
self.circleLayer.removeAllAnimations()
}
//Header完全隐藏,恢复到原始状态
func didCompleteEndRefershingAnimation(result:RefreshResult){
self.circleLayer.strokeEnd = 0.05
self.arrowLayer.hidden = false
textLabel.text = "下拉即可刷新"
然后这样使用
let taobaoHeader = TaoBaoRefreshHeader(frame: CGRectMake(0,0,CGRectGetWidth(self.view.bounds),100))
self.tableView.setUpHeaderRefresh(taobaoHeader) { [weak self] in
delay(1.5, closure: {
self?.models = (self?.models.map({_ in random100()}))! self?.tableView.endHeaderRefreshing(.Success)
})
}
Tips:注意到,TaoBaoRefreshHeader的frame的高度是100,但是distanceToRefresh()却返回60,从而实现了顶部的ImageViwe在刷新的时候隐藏
如果你喜欢这个库,欢迎和我一起把它完善,PullToRefreshKit ,欢迎各种建议,Star,fork。