下拉刷新可以说是每个项目的必须品,最近在做一个
Swift
的项目,本着以锻炼自己的目的,封装了一个下拉刷新库LCRefresh
,在这里分享给大家。
开源库 LCRefresh 的 Demo 以及源码已经上传到 Git
https://github.com/liutongchao/LCRefresh
1、如何使用 LCRefresh
使用 LCRefresh
非常方便,只需添加一行代码。
Pod 支持
pod 'LCRefresh', '~> 0.1.12'
添加下拉刷新 和 上拉加载
import LCRefresh
table.refreshHeader = LCRefreshHeader.init(refreshBlock: {
print("Header 刷新")
//TODO
})
table.refreshFooter = LCRefreshFooter.init(refreshBlock: {
print("Footer 刷新")
//TODO
})
完成刷新
weak var weakSelf = self
if weakSelf!.table.isHeaderRefreshing() {
weakSelf!.table.endHeaderRefreshing()
}
if weakSelf!.table.isFooterRefreshing() {
weakSelf!.table.endFooterRefreshing()
}
数据加载完毕
weakSelf!.table.setDataLoadover()
weakSelf!.table.resetDataLoad()
刷新的效果图
2、如何去封装自己的刷新控件
这里分享一下封装刷新控件的心路历程
① 创建刷新视图,并创建View的各种状态(因为要支持pod xib改为了纯代码)
extension LCRefreshHeader{
/** 各种状态切换 */
func setNomalStatus() {
if activity.isAnimating {
activity.stopAnimating()
}
activity.isHidden = true
contenLab.text = "下拉可以刷新"
image.isHidden = false
UIView.animate(withDuration: 0.2, animations: {
self.image.transform = CGAffineTransform.identity
})
}
func setWaitRefreshStatus() {
if activity.isAnimating {
activity.stopAnimating()
}
activity.isHidden = true
contenLab.text = "松开立即刷新"
image.isHidden = false
UIView.animate(withDuration: 0.2, animations: {
self.image.transform = CGAffineTransform(rotationAngle: CGFloat(-M_PI))
})
}
func setRefreshingStatus() {
activity.isHidden = false
activity.startAnimating()
contenLab.text = "正在刷新数据..."
image.isHidden = true
}
}
②拓展 UIScrollView
动态绑定属性
/** header **/
public var refreshHeader: LCRefreshHeader? {
get{
let key = UnsafeRawPointer.init(bitPattern: "refreshHeader".hashValue)
let result = objc_getAssociatedObject(self, key) as? LCRefreshHeader
return result
}
set{
let key = UnsafeRawPointer.init(bitPattern: "refreshHeader".hashValue)
objc_setAssociatedObject(self, key, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
let result = objc_getAssociatedObject(self, key) as? LCRefreshHeader
if result != nil {
self.addSubview(result!)
}
//添加滑动监测
addOffsetObserver()
weak var weakSelf = self
weakSelf!.panGestureRecognizer.addTarget(weakSelf!, action: #selector(UIScrollView.scrollViewDragging(_:)))
}
}
/** footer **/
public var refreshFooter: LCRefreshFooter? {
get{
let key = UnsafeRawPointer.init(bitPattern: "refreshFooter".hashValue)
let result = objc_getAssociatedObject(self, key) as? LCRefreshFooter
return result
}
set{
let key = UnsafeRawPointer.init(bitPattern: "refreshFooter".hashValue)
objc_setAssociatedObject(self, key, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
let result = objc_getAssociatedObject(self, key) as? LCRefreshFooter
if result != nil {
result!.isHidden = true
self.addSubview(result!)
}
//添加滑动监测
addOffsetObserver()
weak var weakSelf = self
weakSelf!.panGestureRecognizer.addTarget(weakSelf!, action: #selector(UIScrollView.scrollViewDragging(_:)))
}
}
添加视图,监测
ScrollView
滑动,以及drag 响应
③根据ScrollView
的 contentOffset
来区分是下拉刷新还是上拉加载,然后根据 contentOffset
的值来切换刷新View的状态
/** 滑动相关 */
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "contentOffset" {
let offSet = self.contentOffset.y
let scrollHeight = self.bounds.size.height
let inset = self.contentInset
var currentOffset = offSet + scrollHeight - inset.bottom
let maximumOffset = self.contentSize.height
/** 数据未充满屏幕的情况 **/
if maximumOffset < scrollHeight {
currentOffset = offSet + maximumOffset - inset.bottom
}
if offSet < 0 {
/** 下拉刷新 */
guard self.refreshHeader != nil else{
return
}
scrollHeader(offSet)
self.refreshObj = LCRefreshObject.header
}else if currentOffset - maximumOffset > 0 {
/** 上拉刷新 */
guard self.refreshFooter != nil else{
return
}
guard self.refreshFooter!.refreshStatus != .loadover else {
return
}
scrollFooter(currentOffset - maximumOffset)
self.refreshObj = .footer
}else{
/** 无刷新对象 */
self.refreshObj = .none
}
}
}
View状态切换
func scrollHeader(_ offSet: CGFloat) {//参数为负数
guard self.refreshHeader != nil else{
print("Header加载失败")
return
}
guard self.refreshHeader!.refreshStatus != .refreshing else{
return
}
if offSet < -LCRefreshHeaderHeight {
self.refreshHeader!.setStatus(LCRefreshHeaderStatus.waitRefresh)
}else{
self.refreshHeader!.setStatus(LCRefreshHeaderStatus.normal)
}
}
④最后拖拽结束时再根据刷新对象切换View 到刷新状态。
/** 拖拽相关 */
func scrollViewDragging(_ pan: UIPanGestureRecognizer){
if pan.state == .ended{
if self.refreshObj == LCRefreshObject.header {
draggHeader()
}else if self.refreshObj == LCRefreshObject.footer{
draggFooter()
}
}
}
这之间涉及到
contentOffset
值的设置,在这里就不一一明说了,给出的源码里都有。
现在只是开发出了这一种样式,以后会更多的样式以供大家使用,当然,你也可以根据给出的源码自己来修改样式。