
Swift.jpg
下拉刷新可以說(shuō)是每個(gè)項(xiàng)目的必須品,最近在做一個(gè)
Swift的項(xiàng)目,本著以鍛煉自己的目的,封裝了一個(gè)下拉刷新庫(kù)LCRefresh,在這里分享給大家。
開(kāi)源庫(kù) LCRefresh 的 Demo 以及源碼已經(jīng)上傳到 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()
}
數(shù)據(jù)加載完畢
weakSelf!.table.setDataLoadover()
weakSelf!.table.resetDataLoad()
刷新的效果圖

HeaderRefresh.png
2、如何去封裝自己的刷新控件
這里分享一下封裝刷新控件的心路歷程
① 創(chuàng)建刷新視圖,并創(chuàng)建View的各種狀態(tài)(因?yàn)橐С謕od xib改為了純代碼)

LCRefreshHeader
extension LCRefreshHeader{
/** 各種狀態(tài)切換 */
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 = "松開(kāi)立即刷新"
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 = "正在刷新數(shù)據(jù)..."
image.isHidden = true
}
}
②拓展 UIScrollView 動(dòng)態(tài)綁定屬性
/** 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!)
}
//添加滑動(dòng)監(jiān)測(cè)
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!)
}
//添加滑動(dòng)監(jiān)測(cè)
addOffsetObserver()
weak var weakSelf = self
weakSelf!.panGestureRecognizer.addTarget(weakSelf!, action: #selector(UIScrollView.scrollViewDragging(_:)))
}
}
添加視圖,監(jiān)測(cè)
ScrollView滑動(dòng),以及drag 響應(yīng)
③根據(jù)ScrollView的 contentOffset 來(lái)區(qū)分是下拉刷新還是上拉加載,然后根據(jù) contentOffset 的值來(lái)切換刷新View的狀態(tài)
/** 滑動(dòng)相關(guān) */
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
/** 數(shù)據(jù)未充滿屏幕的情況 **/
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{
/** 無(wú)刷新對(duì)象 */
self.refreshObj = .none
}
}
}
View狀態(tài)切換
func scrollHeader(_ offSet: CGFloat) {//參數(shù)為負(fù)數(shù)
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)
}
}
④最后拖拽結(jié)束時(shí)再根據(jù)刷新對(duì)象切換View 到刷新?tīng)顟B(tài)。
/** 拖拽相關(guān) */
func scrollViewDragging(_ pan: UIPanGestureRecognizer){
if pan.state == .ended{
if self.refreshObj == LCRefreshObject.header {
draggHeader()
}else if self.refreshObj == LCRefreshObject.footer{
draggFooter()
}
}
}
這之間涉及到
contentOffset值的設(shè)置,在這里就不一一明說(shuō)了,給出的源碼里都有。
現(xiàn)在只是開(kāi)發(fā)出了這一種樣式,以后會(huì)更多的樣式以供大家使用,當(dāng)然,你也可以根據(jù)給出的源碼自己來(lái)修改樣式。