最近在研究上拉刷新下拉刷新,有點小心得和大家分享下箩绍。
首先我是先在網(wǎng)上搜博客看憔辫,發(fā)現(xiàn)要不就是用原生UIRefreshControl要不就是第三方庫的教程,這都不是我想要的(好的開源庫推薦MJRefresh)金顿。
于是我開始看大神們的源代碼臊泌,了解了下原理。
刷新原理
首先上拉下拉刷新肯定是基于UIScrollView的基礎上的揍拆,包括UITableView其實也是一個UIScrollView渠概。而實現(xiàn)上拉下拉刷新的原理就是UIScrollView中的代理方法
- (void)scrollViewDidScroll:(UIScrollView *)scrollView;// any offset changes
這個方法是在你的scrollView滾動時就會執(zhí)行的方法。
這里有必要說明下scrollView的兩個屬性嫂拴,一個是contentSize高氮,一個是contentOffset慧妄。這里上圖片講解可能直觀一點。

PS:不會用mac的畫圖工具比較丑見諒剪芍。
看圖塞淹,這里的綠色框就是我們的手機屏幕,我們的UI都是呈現(xiàn)在手機屏幕上的罪裹,那么黑色的框就是contentSize饱普。就是說,雖然手機屏幕只有一點大状共,但是我們的scrollView并不是只有一點大的套耕,這個屬性是可以設置的,而我們滾動scrollView其實就是滾動黑色框峡继,這樣看到的界面就會不一樣了冯袍。而圖上標注的紅點就是contentOffset。contentOffset是一個CGPoint碾牌,代表當前屏幕所在位置左上角相對于scrollView.contentSize左上角的橫縱坐標值康愤。
了解了scrollView我們就來說刷新。在
- (void)scrollViewDidScroll:(UIScrollView *)scrollView;// any offset changes
方法中我們應該監(jiān)聽contentOffset的值舶吗,如果他的縱坐標到某個點以上我們就執(zhí)行刷新數(shù)據(jù)征冷,移動到某個點以下我們就執(zhí)行加載數(shù)據(jù)。具體看代碼可能更好理解誓琼。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if ([scrollView isEqual:_tableView]) {
if (scrollView.contentOffset.y < -50 ) {
//下拉刷新方法
}
if (scrollView.contentOffset.y > 800 ) {
//上拉加載方法
}
}
}
原理很簡單检激,但是實現(xiàn)的話還是有很多細節(jié)需要考慮的,比如scrollView.contentOffset.y < -50
的情況是很多的腹侣,當用戶快速上拉到-100時叔收,可能在-51執(zhí)行一次刷新方法,在-52執(zhí)行一次傲隶,-53執(zhí)行一次今穿。。伦籍。。腮出。帖鸦。這肯定是不合理的。
所以我們需要節(jié)流胚嘲。這里提兩個我的個人觀點作儿,一個是開線程執(zhí)行刷新辦法,如果線程存在不會新開一個馋劈,這樣可以保證刷新方法執(zhí)行一次攻锰。還有一種是用KVO監(jiān)聽用戶手指松開的動作晾嘶,松開的時候再刷新,或者- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
用這個代理方法娶吞,他會幫你監(jiān)聽手指是否松開垒迂。這兩個方法我都沒試過,大家可以自己嘗試下妒蛇,有錯希望反饋給我机断,共同進步。
但是如果僅僅是判定松開我覺得是滿足不了需求的绣夺。我現(xiàn)在希望就是當用戶快下滑到底部時就自動加載新數(shù)據(jù)吏奸,那我應該怎么實現(xiàn)呢?總不能用戶一拉到底不松手就不加載吧陶耍。
RAC一個方法實現(xiàn)刷新
我想到了RAC奋蔚。這個想法可能有點非主流,所以肯定有邏輯考慮不周的地方烈钞,希望各位指出泊碑。這里我先宏定義了下,為了縮短代碼
#define VIEWHEIGHT self.view.frame.size.height
然后就是RAC了棵磷。
[[[RACObserve(self.tableView, contentOffset) map:^id(id value) {
if (self.tableView.contentOffset.y < -50) {
return @"1";
}
if (self.tableView.contentOffset.y > self.tableView.contentSize.height - VIEWHEIGHT * 1.5 && self.tableView.contentSize.height - VIEWHEIGHT * 1.5 > 0) {
return @"2";
}else{
return @"0";
}
}] distinctUntilChanged] subscribeNext:^(id x) {
debugLog(@"%@", x);
if ([x integerValue] == 1) {
[self netWork];
}else if ([x integerValue] == 2){
[self loadMoreData];
}
}];
這里是用了RAC KVO的寫法蛾狗,不會的可以點我文章復習下。首先寫了一個通知監(jiān)聽tableView的contentOffset仪媒,如果發(fā)生變化立刻進入map產(chǎn)生的映射中執(zhí)行map中的方法沉桌。我給情況分了類,如果用戶下拉算吩,返回1留凭,如果上拉快到底部時返回2。并且在映射完成后用了distinctUntilChanged
屬性偎巢,當我的映射值不產(chǎn)生變化時是不會傳遞映射值的蔼夜。這樣當用戶拉倒需要刷新的位置,只會發(fā)一個信號給訂閱者压昼,只會執(zhí)行一次刷新數(shù)據(jù)的方法求冷,這樣我所有的需求就迎刃而解了。
如果有更好的辦法希望大家給我指出窍霞,謝謝大家匠题。也歡迎來我的博客看看