用Swift实现淘宝和大众点评的下拉刷新

来自Leo的原创博客,转载请著名出处

我的StackOverflow

profile for Leo on Stack Exchange, a network of free, community-driven Q&A sites

我的Github
https://github.com/LeoMobileDeveloper

效果

淘宝

大众点评

项目地址

其中

  • 大众点评的下拉刷新用了50行左右代码
  • 淘宝的下拉刷新用了90行左右代码

完整的代码地址:PullToRefreshKit


Tips:用Swift 2.2写的,所以需要XCode 7.3来运行。

PullToRefreshKit

这是我用纯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)
        })
    }

淘宝下拉刷新

首先分析视图架构

整个下拉刷新的界面如下

其中

  1. 是CAShapeLayer,随着滑动,动态调整绘制过程,刷新的时候转圈圈
  2. 是CAShapeLayer,静态的,当刷新的时候隐藏
  3. 简单的UILabel
  4. 在刷新界面上面还有一个Imageview,创建一个Imageview放置到上面即可

所以,完整的代码如下

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。

你可能感兴趣的:(ios,swift,淘宝,下拉刷新,大众点评)