利用RAC一句話實現上拉刷新下拉刷新

puiz9449 8年前發布 | 17K 次閱讀 iOS開發 移動開發

最近在研究上拉刷新下拉刷新,有點小心得和大家分享下。

首先我是先在網上搜博客看,發現要不就是用原生UIRefreshControl要不就是第三方庫的教程,這都不是我想要的(好的開源庫推薦 MJRefresh )。

于是我開始看大神們的源代碼,了解了下原理。

刷新原理

首先上拉下拉刷新肯定是基于UIScrollView的基礎上的,包括UITableView其實也是一個UIScrollView。而實現上拉下拉刷新的原理就是UIScrollView中的代理方法

- (void)scrollViewDidScroll:(UIScrollView *)scrollView;// any offset changes

這個方法是在你的scrollView滾動時就會執行的方法。

這里有必要說明下scrollView的兩個屬性,一個是contentSize,一個是contentOffset。這里上圖片講解可能直觀一點。

PS:不會用mac的畫圖工具比較丑見諒。

看圖,這里的綠色框就是我們的手機屏幕,我們的UI都是呈現在手機屏幕上的,那么黑色的框就是contentSize。就是說,雖然手機屏幕只有一點大,但是我們的scrollView并不是只有一點大的,這個屬性是可以設置的,而我們滾動scrollView其實就是滾動黑色框,這樣看到的界面就會不一樣了。而圖上標注的紅點就是contentOffset。contentOffset是一個CGPoint,代表當前屏幕所在位置左上角相對于scrollView.contentSize左上角的橫縱坐標值。

了解了scrollView我們就來說刷新。在 - (void)scrollViewDidScroll:(UIScrollView *)scrollView;// any offset changes 方法中我們應該監聽contentOffset的值,如果他的縱坐標到某個點以上我們就執行刷新數據,移動到某個點以下我們就執行加載數據。具體看代碼可能更好理解。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView 
{
if ([scrollView isEqual:_tableView]) {
    if (scrollView.contentOffset.y < -50 ) {
    //下拉刷新方法
    }
    if (scrollView.contentOffset.y > 800 ) {
    //上拉加載方法
    }
  }
}

原理很簡單,但是實現的話還是有很多細節需要考慮的,比如 scrollView.contentOffset.y < -50 的情況是很多的,當用戶快速上拉到-100時,可能在-51執行一次刷新方法,在-52執行一次,-53執行一次。。。。。。這肯定是不合理的。

所以我們需要節流。這里提兩個我的個人觀點,一個是開線程執行刷新辦法,如果線程存在不會新開一個,這樣可以保證刷新方法執行一次。還有一種是用KVO監聽用戶手指松開的動作,松開的時候再刷新,或者 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 用這個代理方法,他會幫你監聽手指是否松開。這兩個方法我都沒試過,大家可以自己嘗試下,有錯希望反饋給我,共同進步。

但是如果僅僅是判定松開我覺得是滿足不了需求的。我現在希望就是當用戶快下滑到底部時就自動加載新數據,那我應該怎么實現呢?總不能用戶一拉到底不松手就不加載吧。

RAC一個方法實現刷新

我想到了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的寫法,不會的可以點我文章復習下。首先寫了一個通知監聽tableView的contentOffset,如果發生變化立刻進入map產生的映射中執行map中的方法。我給情況分了類,如果用戶下拉,返回1,如果上拉快到底部時返回2。并且在映射完成后用了 distinctUntilChanged 屬性,當我的映射值不產生變化時是不會傳遞映射值的。這樣當用戶拉倒需要刷新的位置,只會發一個信號給訂閱者,只會執行一次刷新數據的方法,這樣我所有的需求就迎刃而解了。

如果有更好的辦法希望大家給我指出,謝謝大家。

來自: http://cbsfly.github.io/ios/rac-refresh/

 本文由用戶 puiz9449 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!