以下代码中的`约束布局框架`使用的是SnapKit
自定下拉刷新控件
SHRefreshControl.swift
//
// SHRefreshControl.swift
// weibo
//
// Created by 邵瑞波 on 16/1/17.
// Copyright © 2016年 邵瑞波. All rights reserved.
//
import UIKit
// 控件当前状态
enum SHRefreshControlState: Int {
case Normal = 0 // 正常状态
case Pulling = 1 // 松手可以刷新的状态
case Refreshing = 2 // 正在刷新的状态
}
class SHRefreshControl: UIControl {
var top: CGFloat = 0.0
var currRefreshState: SHRefreshControlState {
return refreshState
}
// 之前刷新状态
private var oldRefreshState: SHRefreshControlState = .Normal
// 当前刷新状态
private var refreshState: SHRefreshControlState = .Normal {
didSet {
switch refreshState {
case .Normal: // 正常状态
arrowIcon.hidden = false
indicatorView.stopAnimating()
labelTip.text = "下拉刷新"
UIView.animateWithDuration(0.25, animations: { () -> Void in
self.arrowIcon.transform = CGAffineTransformIdentity
})
if oldRefreshState == .Refreshing {
print("xxxxxxxxx")
UIView.animateWithDuration(0.25, animations: { () -> Void in
self.scrollView?.contentInset.top -= self.frame.size.height
})
}
oldRefreshState = refreshState
case .Pulling: // 释放可以刷新的状态
arrowIcon.hidden = false
labelTip.text = "释放加载"
UIView.animateWithDuration(0.25, animations: { () -> Void in
self.arrowIcon.transform = CGAffineTransformMakeRotation(CGFloat(M_PI))
})
oldRefreshState = refreshState
case .Refreshing: // 正在刷新的状态
arrowIcon.hidden = true
arrowIcon.transform = CGAffineTransformIdentity
indicatorView.startAnimating()
labelTip.text = "正在加载"
// 这里要提前一步设置,否则后续发送事件时,是同步调用,这是的原来状态还没有改变,在.Normal中执行会出现问题
oldRefreshState = refreshState
// UIView.animateWithDuration(0.25, animations: { () -> Void in
// self.scrollView?.contentInset.top += self.frame.size.height
// })
UIView.animateWithDuration(0.25, animations: { () -> Void in
self.scrollView?.contentInset.top += self.frame.size.height
}, completion: { (_) -> Void in
self.sendActionsForControlEvents(UIControlEvents.ValueChanged)
})
// 如果发送事件的代码放无法保证与动画线程同步,则无法保证在控制器中处理事件时,动画已经执行完毕,这样会出现
// 下拉刷新按钮没执行一次,则向下移动一段距离无法回去。
// 这里要提前一步设置,否则后续发送事件时,是同步调用,这是的原来状态还没有改变,在.Normal中执行会出现问题
// sendActionsForControlEvents(UIControlEvents.ValueChanged)
}
// 这里设置原来的状态放到每个case分支中单独设置,因为某些逻辑需要,都放到这里设置无法实现!!!!
// oldRefreshState = refreshState
}
}
private var scrollView: UIScrollView?
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
backgroundColor = UIColor(white: 0.99, alpha: 1)
let height: CGFloat = 40
frame.origin.x = 0
frame.origin.y = -height
frame.size.height = height
frame.size.width = kScreenW // 这个在被添加到父视图时会重新调整
addSubview(arrowIcon)
addSubview(indicatorView)
addSubview(labelTip)
let marginCenterX = 10
labelTip.snp_makeConstraints { (make) -> Void in
make.centerY.equalTo(self)
make.centerX.equalTo(self).offset(marginCenterX)
}
arrowIcon.snp_makeConstraints { (make) -> Void in
make.centerY.equalTo(self)
make.trailing.equalTo(labelTip.snp_leading)
}
indicatorView.snp_makeConstraints { (make) -> Void in
make.center.equalTo(arrowIcon)
}
}
// 箭头
private var arrowIcon: UIImageView = UIImageView(image: UIImage(named: "tableview_pull_refresh"))
// 菊花
private var indicatorView: UIActivityIndicatorView = {
let indicatorView = UIActivityIndicatorView()
indicatorView.hidesWhenStopped = true
indicatorView.activityIndicatorViewStyle = .Gray
return indicatorView
}()
// 文字
private var labelTip: UILabel = {
let label = UILabel(textColor: UIColor.lightGrayColor(), fontSize: 14)
label.text = "下拉刷新"
return label
}()
override func willMoveToSuperview(newSuperview: UIView?) {
super.willMoveToSuperview(newSuperview)
//scrollView?.removeObserver(self, forKeyPath: "contentOffset")
if let scrollView = newSuperview as? UIScrollView {
self.scrollView = scrollView
// 添加对父视图`contentOffset`的监听
self.scrollView?.addObserver(self, forKeyPath: "contentOffset", options: NSKeyValueObservingOptions.New, context: nil)
// 根据父视图,调整自身宽度
frame.size.width = scrollView.frame.size.width
}
}
func endRefreshing() {
refreshState = .Normal
}
// 如果使用 kvo 监听了 属性,则 该方法必须实现,否则会崩溃
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if refreshState == .Refreshing {
return
}
if !(object as? NSObject)!.isKindOfClass(UIScrollView.self) {
return
}
let scrollView = object as! UIScrollView
let offSet = scrollView.contentOffset
let top = scrollView.contentInset.top
let height = self.frame.size.height
// print("offSet=[\(offSet)]---[\(-(top + height))]")
if scrollView.dragging {
if offSet.y < -(top + height) {
// 箭头翻转,如果在
self.refreshState = .Pulling
// 此时如果释放的话,下次执行会进入到 LOOP_REFRESHE 这里
} else {
self.refreshState = .Normal
}
} else {
// LOOP_REFRESHE
if oldRefreshState == .Refreshing {
return
}
// 判断如果当前scrollView的偏移量小于,如下值,则翻转箭头,发送事件,开始刷新
if oldRefreshState == .Pulling {
refreshState = .Refreshing
} else {
refreshState = .Normal
}
}
}
deinit {
scrollView?.removeObserver(self, forKeyPath: "contentOffset")
}
}
在控制器中的代码如下:
// 创建控件
let shRefreshControl: SHRefreshControl = SHRefreshControl()
// 设置监听
shRefreshControl.addTarget(self, action: "refreshPro:", forControlEvents: UIControlEvents.ValueChanged)
// 添加到父类tableView
tableView.addSubview(shRefreshControl)