Day19 - 自定义下拉刷新

效果:

最终效果

<br />
<br />

1、UI布局

布局

<br />

2、代码实现,为了Controller中的代码不那么臃肿,我们稍微封装一下代码

2.1 封装dataSource

  • 1 新建一个类,继承NSobject
  • 2 导入UITableViewDatasource
  • 3 实现代理方法
  • 4 封装一个对外公开的类方法:此时应该至少需要Identifier、数据源、有没有Section、回调(闭包)
  • 5 提供一个结构体
  • 6 提供一个获取数据(模型)的方法
    代码如下
//
//  TBDataSource.swift
//  CustomPullToRefresh
//
//  Created by ios on 16/9/26.
//  Copyright © 2016年 ios. All rights reserved.
//

import UIKit
/**
 设置Section样式,默认 Single
 */
public enum TBSectionStyle : Int {
    
    ///Default 默认没有多个Section
    case Section_Single
    /// 有多个Section
    case Section_Has
}


class TBDataSource: NSObject,UITableViewDataSource {
    
    private var sectionStyle : TBSectionStyle = .Section_Single
    private var data : NSArray?
    private var identifier : String = "null"
    private var cellBlock : ((cell : AnyObject, item : AnyObject) -> ())?
    
    /**
     快速创建一个数据源,需要提前注册,数组和style要对应
     
     - parameter identifier: 标识
     - parameter data:       数据
     - parameter style:      类型
     - parameter cell:       回调
     
     - returns: 数据源对象(dataSource)
     */
    static func cellIdentifierWith(identifier : String , data : NSArray , style : TBSectionStyle , cell : ((cell : AnyObject, item : AnyObject) -> Void)) -> TBDataSource {
        let source = TBDataSource()
        
        source.sectionStyle = style
        source.data = data
        source.identifier = identifier
        source.cellBlock = cell
        
        return source
        
    }
    /**
     返回数据
     
     - parameter indexPath: indexPath
     
     - returns: 数据
     */
    private func itemWithIndexPath(indexPath : NSIndexPath) -> AnyObject{
        if sectionStyle == .Section_Single {
            return data![indexPath.row]
        }
        else{
            return data![indexPath.section][indexPath.row]
        }
    }
    
    /**
     返回有多少个Section
     
     - parameter tableView: tableView
     
     - returns: section
     */
    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        if sectionStyle == .Section_Single {
            return 1
        }
        return (data?.count)!
    }
    
    /**
     返回对应Section的rows
     
     - parameter tableView: tableView
     - parameter section:   section
     
     - returns: rows
     */
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
        if sectionStyle == .Section_Single {
            return (data?.count)!
        }else{
            return (data?[section].count)!
        }
    }
    /**
     返回cell,并用闭包把cell封装到外面,提供样式设置
     
     - parameter tableView: tableView
     - parameter indexPath: indexPath
     
     - returns: cell
     */
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath)
        if let block = cellBlock {
            block(cell: cell, item: itemWithIndexPath(indexPath))
        }
        return cell
    }
}

<br />
封装好DataSource之后,ViewController中声明一个属性,此时对cell外观操作,都在这里处理

 /// 数据源
    private lazy var dataSource : TBDataSource = {
       let source = TBDataSource.cellIdentifierWith("iconCell", data: [["😂", "🤗", "😳", "😌", "😊"],["😂", "🤗", "😳", "😌", "😊"],["😂", "🤗", "😳", "😌", "😊"]], style: TBSectionStyle.Section_Has, cell: { (cell: AnyObject, item: AnyObject) -> () in
            let newCell = cell as! UITableViewCell
            let newItem = item as! String
            newCell.textLabel!.text = newItem
            newCell.textLabel?.font = UIFont(name: "Apple Color Emoji", size: 40)
            newCell.textLabel?.textAlignment = NSTextAlignment.Center
       })
        return source
    }()

<br />
声明属性之后,只需要执行下面的一行代码便可以展示出数据源了

tableView.dataSource = dataSource

3、封装动画

  • 创建一个xib
xib
  • 创建一个类,继承UIRefreshControl
  • 提供一个加载xib的类方法
  • 在类方法中把xib加载进来的views存入数组,便于做动画
  • 实现动画,这里使用transfrom,你们也可以自定义其他的动画
    代码试下如下:
//
//  CustomRefreshView.swift
//  CustomPullToRefresh
//
//  Created by ios on 16/9/26.
//  Copyright © 2016年 ios. All rights reserved.
//

import UIKit
/// 自定义刷新控件
class CustomRefreshView: UIRefreshControl {

    private var xibViews : Array<UIView> = []
    private var isAnimating : Bool = false
    private var currentColorIndex = 0
    private var currentLabelIndex = 0
    static func addViewsForNib( name : String) -> CustomRefreshView{
        let content = NSBundle.mainBundle().loadNibNamed(name, owner: self, options: nil).first as! UIView
        
        let cus = CustomRefreshView()
        cus.addSubview(content)
        
        for i in 0..<content.subviews.count {
            cus.xibViews.append(content.subviews[i])
            cus.xibViews[i].layer.cornerRadius = (cus.xibViews[i].bounds.width + cus.xibViews[i].bounds.height) * 0.25
            cus.xibViews[i].clipsToBounds = true
        }
        
        return cus
    }
    
    /**
     向外面提供一个开始动画的方法
     */
    func startAnimation(){
        animateRefreshStep1()
    }
    
    ///动画设置 外面不需要知道,所以都设置成为私有方法
    private func animateRefreshStep1() {
        
        isAnimating = true
        
        UIView.animateWithDuration(0.1, delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: { () -> Void in
            
            self.xibViews[self.currentLabelIndex].transform = CGAffineTransformMakeRotation(CGFloat(M_PI_4))
            self.xibViews[self.currentLabelIndex].backgroundColor = self.getNextColor()
            
            }, completion: { (finished) -> Void in
                
                UIView.animateWithDuration(0.05, delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: { () -> Void in
                    self.xibViews[self.currentLabelIndex].transform = CGAffineTransformIdentity
                    
                    }, completion: { (finished) -> Void in
                        self.currentLabelIndex += 1
                        
                        if self.currentLabelIndex < self.xibViews.count {
                            self.animateRefreshStep1()
                        }
                        else {
                            self.animateRefreshStep2()
                        }
                })
        })
    }
    
    ///动画设置 外面不需要知道,所以都设置成为私有方法
    private func animateRefreshStep2() {
        UIView.animateWithDuration(0.40, delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: { () -> Void in
            
            self.xibViews[0].transform = CGAffineTransformMakeScale(1.5, 1.5)
            self.xibViews[1].transform = CGAffineTransformMakeScale(1.5, 1.5)
            self.xibViews[2].transform = CGAffineTransformMakeScale(1.5, 1.5)
            self.xibViews[3].transform = CGAffineTransformMakeScale(1.5, 1.5)
            self.xibViews[4].transform = CGAffineTransformMakeScale(1.5, 1.5)
            self.xibViews[5].transform = CGAffineTransformMakeScale(1.5, 1.5)
            self.xibViews[6].transform = CGAffineTransformMakeScale(1.5, 1.5)
            self.xibViews[7].transform = CGAffineTransformMakeScale(1.5, 1.5)
            
            }, completion: { (finished) -> Void in
                
                UIView.animateWithDuration(0.25, delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: { () -> Void in
                    self.xibViews[0].transform = CGAffineTransformIdentity
                    self.xibViews[1].transform = CGAffineTransformIdentity
                    self.xibViews[2].transform = CGAffineTransformIdentity
                    self.xibViews[3].transform = CGAffineTransformIdentity
                    self.xibViews[4].transform = CGAffineTransformIdentity
                    self.xibViews[5].transform = CGAffineTransformIdentity
                    self.xibViews[6].transform = CGAffineTransformIdentity
                    self.xibViews[7].transform = CGAffineTransformIdentity
                    
                    }, completion: { (finished) -> Void in
                        if self.refreshing {
                            self.currentLabelIndex = 0
                            self.animateRefreshStep1()
                        }
                        else {
                            self.isAnimating = false
                            self.currentLabelIndex = 0
                            for i in 0 ..< self.xibViews.count {
                                self.xibViews[i].transform = CGAffineTransformIdentity
                                self.xibViews[i].backgroundColor = UIColor.clearColor()
                            }
                        }
                })
        })
    }
    ///动画设置 外面不需要知道,所以都设置成为私有方法
    private func getNextColor() -> UIColor {
        var colorsArray: Array<UIColor> = [UIColor.magentaColor(), UIColor.brownColor(), UIColor.yellowColor(), UIColor.redColor(), UIColor.greenColor(), UIColor.blueColor(), UIColor.orangeColor()]
        
        if currentColorIndex == colorsArray.count {
            currentColorIndex = 0
        }
        
        let returnColor = colorsArray[currentColorIndex]
        currentColorIndex += 1
        
        return returnColor
    }
}


<br />

完成对动画的封装之后,Controller中的代码如下

//
//  ViewController.swift
//  CustomPullToRefresh
//
//  Created by ios on 16/9/26.
//  Copyright © 2016年 ios. All rights reserved.
//

import UIKit

class ViewController: UIViewController ,UITableViewDelegate{

    @IBOutlet weak var tableView: UITableView!
    
    /// 数据源
    private lazy var dataSource : TBDataSource = {
       let source = TBDataSource.cellIdentifierWith("iconCell", data: [["😂", "🤗", "😳", "😌", "😊"],["😂", "🤗", "😳", "😌", "😊"],["😂", "🤗", "😳", "😌", "😊"]], style: TBSectionStyle.Section_Has, cell: { (cell: AnyObject, item: AnyObject) -> () in
            let newCell = cell as! UITableViewCell
            let newItem = item as! String
            newCell.textLabel!.text = newItem
            newCell.textLabel?.font = UIFont(name: "Apple Color Emoji", size: 40)
            newCell.textLabel?.textAlignment = NSTextAlignment.Center
       })
        return source
    }()
    
    private lazy var refreshControl : CustomRefreshView = CustomRefreshView.addViewsForNib("RefreshContents")
    
    private var isAnimating : Bool = false
    
    var timer: NSTimer!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.dataSource = dataSource
        tableView.delegate = self
        tableView.rowHeight = 60
        
        tableView.addSubview(refreshControl)
        
    }
    
    func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
        if refreshControl.refreshing {
            if !isAnimating {
                doSomething()
                refreshControl.startAnimation()
            }
        }
    }
    
    /**
     开启定时器
     */
    private func doSomething() {
        timer = NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: #selector(ViewController.endedOfWork), userInfo: nil, repeats: true)
    }
    /**
     定时器结束,关闭动画
     */
    @objc private func endedOfWork() {
        refreshControl.endRefreshing()
        timer.invalidate()
        timer = nil
    }
}


本文Demo

Demo - CustomPullToRefresh

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容