1. 程式人生 > >用Swift實現淘寶和大眾點評的下拉重新整理

用Swift實現淘寶和大眾點評的下拉重新整理

來自Leo的原創部落格,轉載請著名出處

我的StackOverflow

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

效果

淘寶

大眾點評

專案地址

其中

  • 大眾點評的下拉重新整理用了50行左右程式碼
  • 淘寶的下拉重新整理用了90行左右程式碼


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。