最近陷入了項目中一個日歷月視圖與周視圖切換效果的實現(xiàn),長時間沒有實現(xiàn)想要的效果弯蚜,沮喪至極孔轴。煩請有好想法的同學(xué)指點一二,在線等@留什么白碎捺。
結(jié)束例行的啰嗦路鹰,進(jìn)入正題。
大家可能都用過MJRefresh,十分之方便收厨,李明杰老師借助runtime的特性晋柱,極盡之能,讓千千萬萬小白開發(fā)者和遵循不重復(fù)制造輪子原則的開發(fā)者用最少的代碼就實現(xiàn)了界面下拉刷新的功能诵叁。我們今天不談runtime的黑魔法雁竞,而是UITableView最常見的刷新數(shù)據(jù)方式下拉刷新的實現(xiàn)過程。
首先黎休,我們定義三個枚舉值
typedef NS_ENUM(NSUInteger, RefreshState) {
RefreshStateNormal,//正常
RefreshStatePulling,//釋放即可刷新
RefreshStateLoading,//加載中
};
分別表示正常狀態(tài)浓领、釋放即可刷新狀態(tài)玉凯、加載中狀態(tài)
為了簡單說明,我們只用一個UILabel來表現(xiàn)就可以联贩,把它放在內(nèi)容上方漫仆,也就是正常情況下看不到的地方,只有下拉的時候才能看到泪幌。
然后我們估算一個大體的距離盲厌,從而確定觸發(fā)加載狀態(tài)的臨界點。
簡單描述一下將要實現(xiàn)的情形:
我們往下滑動視圖祸泪,如果視圖下滑到觸發(fā)點吗浩,狀態(tài)變?yōu)?code>RefreshStatePulling,即標(biāo)簽文字變?yōu)椤八墒旨纯伤⑿隆薄?br>
接下來有兩種狀況會發(fā)生:一,松開手没隘;二懂扼,不松手,又向初始位置滑右蒲。第一種情況阀湿,當(dāng)你松開手時,因為UIScrollView有回彈效果瑰妄,視圖會往上滑動陷嘴,當(dāng)?shù)竭_(dá)觸發(fā)點時,狀態(tài)改為RefreshStateLoading
,即視圖停在當(dāng)前位置间坐,不受回彈作用影響灾挨,標(biāo)簽文字變?yōu)椤凹虞d中...”,等加載動作完成之后,手動觸發(fā)復(fù)原動作竹宋,視圖滑到原始狀態(tài) RefreshStateNormal
,同時標(biāo)簽文字變?yōu)椤吕⑿隆统危坏诙N情況,你沒有松手蜈七,然后又往回滑動浴骂,當(dāng)?shù)竭_(dá)觸發(fā)點以上時,狀態(tài)變?yōu)槌跏紶顟B(tài)RefreshStateNormal
宪潮。
如果你對上述過程不是很了解的話溯警,那你隨便找個帶下拉刷新的應(yīng)用試試就行了,不用溫柔狡相,越暴力越好梯轻。弄清上述過程是實現(xiàn)整個流程的基礎(chǔ)。
接下來要做的就是決定什么時候改變狀態(tài)了尽棕,參照上述過程喳挑,我們在UIScrollView的代理函數(shù)scrollViewDidScroll:
監(jiān)控contentOffset
的變化,再結(jié)合isDragging
屬性就可以改變狀態(tài)了。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
if(scrollView.contentOffset.y < -SwitchPoint-self.originalInsetTop){
if(self.refreshState == RefreshStateNormal){//小于臨界值(在觸發(fā)點以下)伊诵,如果狀態(tài)是正常就轉(zhuǎn)為下拉刷新单绑,如果正在刷新或者已經(jīng)是下拉刷新則不變
self.refreshState = RefreshStatePulling;
}
}else{//大于臨界值(在觸發(fā)點以上,包括觸發(fā)點)
if(scrollView.isDragging){//手指沒有離開屏幕
if(self.refreshState == RefreshStatePulling){//原來是下拉的話變成正常曹宴,原來是刷新或者正常的話不變
self.refreshState = RefreshStateNormal;
}
}else{//手指離開屏幕
if(self.refreshState == RefreshStatePulling){//原來是下拉的話變成加載中搂橙,原來是加載中或者正常的話不變
self.tableView.contentInset = UIEdgeInsetsMake(self.originalInsetTop+SwitchPoint, 0, 0, 0);//改變contentInset的值就可以取消回彈效果停留在當(dāng)前位置了 關(guān)于contentIinset的介紹,可以查看我的上一篇文章
self.refreshState = RefreshStateLoading;
}
}
}
}
這段代碼決定了UIScrollView每個時刻的狀態(tài)笛坦,那么接下來的就簡單多了区转,只要重寫一下setRefreshState:
方法就可以了。
- (void)setRefreshState:(RefreshState)refreshState{
_refreshState = refreshState;
switch (refreshState) {
case RefreshStateNormal:
self.pulldownLabel.text = @"下拉刷新";
[self.pulldownLabel sizeToFit];
break;
case RefreshStateLoading:
self.pulldownLabel.text = @"正在刷新...";
[self.pulldownLabel sizeToFit];
if(self.refreshBlock){
self.refreshBlock();//這里就是你要執(zhí)行的耗時的操作
}
break;
case RefreshStatePulling:
self.pulldownLabel.text = @"松開即可刷新";
[self.pulldownLabel sizeToFit];
break;
default:
break;
}
}
看一下我們的效果:
當(dāng)然你會說這不夠diao,不夠炫酷版扩,人家的效果都是這樣:
又被你發(fā)現(xiàn)了废离,??,我滿(hao)是(wu)虔(jie)誠(cao)地盜了大神的圖礁芦,@M了個J蜻韭,大神,膝蓋已發(fā)貨柿扣,請驗收湘捎。
那么我們怎么實現(xiàn)這樣的效果呢,很簡單窄刘,在上邊設(shè)置狀態(tài)的代碼里添加動畫就行了。來一段偽的代碼:
case RefreshStateNormal:
self.pulldownLabel.text = @"下拉刷新";
[UIView animateWithDuration:0.3 animations:^{
self.arrowImage.transform = CGAffineTransformMakeRotation(M_PI/2);//箭頭旋轉(zhuǎn)180o
}];
break;
還有一點需要強(qiáng)調(diào)舷胜,在scrollViewDidScroll:
里保持加載狀態(tài)時修改了contentInset
,所以取消加載狀態(tài)恢復(fù)原狀時只需...如此如此:
- (void)endRefresh{
if(self.refreshState == RefreshStateLoading){
self.refreshState = RefreshStateNormal;
[UIView animateWithDuration:0.3 animations:^{
self.tableView.contentInset = UIEdgeInsetsMake(self.originalInsetTop, 0, 0, 0);
} completion:nil];
}
}
OK娩践,希望看到這里你能理解下拉刷新的整個過程及原理了,歡迎指正和打擊烹骨。