利用預(yù)加載讓分頁加載不再繁瑣之單個分頁講解

大概是項目里太多的分頁加載數(shù)據(jù),所以一個簡單、快捷、高效分頁加載會使你那么的愉悅.

大概就是這么絲滑

images
images

github鏈接:JSLoadMoreService

用法講解

屬性預(yù)覽

NSObject+LoadMoreService.h

/**
 *  分頁請求數(shù)量
 */
static NSInteger const PerPageMaxCount = 20;

@interface NSObject (LoadMoreService)
/**
 *  每次請求追加的indexpaths
 */
@property (nonatomic, strong) NSMutableArray *appendingIndexpaths;
/**
 *  數(shù)據(jù)數(shù)組
 */
@property (nonatomic, strong) NSMutableArray *dataArray;
/**
 *  原始請求數(shù)據(jù)
 */
@property (nonatomic, strong) id orginResponseObject;
/**
 *  當(dāng)前頁碼
 */
@property (nonatomic, assign) NSInteger currentPage;
/**
 *  是否請求中
 */
@property (nonatomic, assign) BOOL isRequesting;
/**
 *  是否數(shù)據(jù)加載完
 */
@property (nonatomic, assign) BOOL isNoMoreData;

/**
 *  單一請求分頁加載數(shù)據(jù)
 *
 *  @param baseURL               請求地址
 *  @param para                  請求參數(shù)
 *  @param keyOfArray            取數(shù)組的key(注:多層請用/分隔)
 *  @param classNameOfModelArray 序列化model的class_name
 *  @param isReload              (YES:刷新、NO:加載更多)
 *
 *  @return RACSingal
 */
- (RACSignal *)js_singalForSingleRequestWithURL:(NSString *)baseURL
                                           para:(NSMutableDictionary *)para
                                     keyOfArray:(NSString *)keyOfArray
                          classNameOfModelArray:(NSString *)classNameOfModelArray
                                       isReload:(BOOL)isReload;


@end

UITableView+Preload.h

/**
 *  預(yù)加載觸發(fā)的數(shù)量
 */
static NSInteger const PreloadMinCount = 10;

typedef void(^PreloadBlock)(void);

typedef void(^ReloadBlock)(void);

@interface UITableView (Prereload)
/**
 *  預(yù)加載回調(diào)
 */
@property (nonatomic, copy  ) PreloadBlock js_preloadBlock;
/**
 *  tableview數(shù)據(jù)
 */
@property (nonatomic, strong) NSMutableArray *dataArray;
/**
 *  計算當(dāng)前index是否達(dá)到預(yù)加載條件并回調(diào)
 *
 *  @param currentIndex row or section
 */
- (void)preloadDataWithCurrentIndex:(NSInteger)currentIndex;
/**
 *  上拉刷新
 *
 *  @param js_reloadBlock 刷新回調(diào)
 */
- (void)headerReloadBlock:(ReloadBlock)js_reloadBlock;
/**
 *  結(jié)束上拉刷新
 */
- (void)endReload;

如何調(diào)用

建一個viewModel類

這里處理數(shù)據(jù)的邏輯,所以寫了方法 - (RACSignal *)siganlForJokeDataIsReload:(BOOL)isReload

下面就是怎樣調(diào)用分類的方法:

    
    RACReplaySubject *subject = [RACReplaySubject subject];

    [[self js_singalForSingleRequestWithURL:Test_Page_URL
                                      para:nil
                                keyOfArray:@"pdlist"
                     classNameOfModelArray:@"JSGoodListModel"
                                  isReload:isReload] subscribeNext:^(id  _Nullable x) {
        /**
         *  x : 分類方法(js_singalForSingleRequestWithURL:...)里 sendNext 傳過來的數(shù)組
         *  你可以對每次傳過來的數(shù)組的元素"再加工",知道達(dá)到你的要求后 再 sendNext
         */
        //...
        [subject sendNext:x];
    } error:^(NSError * _Nullable error) {
        [subject sendError:error];
    } completed:^{
        /**
         *  走到這里為,每次分頁請求所有邏輯處理完畢
         */
        [subject sendCompleted];
    }];
    
    return subject;
}

VC調(diào)用:

整個方法:

- (void)requestGoodListIsReload:(BOOL)isReload{
    
    kWeakSelf(self)
    [[self.viewModel siganlForJokeDataIsReload:isReload] subscribeError:^(NSError * _Nullable error) {
        
    } completed:^{
        kStrongSelf(self)
        self.listTableView.dataArray = self.viewModel.dataArray;
        [self.listTableView reloadData];
        [self.listTableView endReload];
    }];
}

tableview里調(diào)用預(yù)加載

繪制cell代理里調(diào)用,根據(jù)你的需求是row or section

    forRowAtIndexPath:(NSIndexPath *)indexPath
{
    JSGoodListModel *model = self.dataArray[indexPath.row];
    cell.textLabel.text = model.title;
    cell.detailTextLabel.text = [NSString stringWithFormat:@"¥%@",model.price];
    /**
     *  根據(jù)當(dāng)期index計算是否回調(diào)preloadblock
     */
    [self preloadDataWithCurrentIndex:indexPath.row];
}

配置tableview的上拉刷新和預(yù)加載:

- (JSListTableView *)listTableView{
    
    if (!_listTableView) {
        _listTableView = [[JSListTableView alloc] initWithFrame:self.view.bounds
                                                          style:UITableViewStyleGrouped];
        [self.view addSubview:_listTableView];
        
        kWeakSelf(self)
        /**
         *  刷新
         */
        [_listTableView headerReloadBlock:^{
            kStrongSelf(self)
            [self requestGoodListIsReload:YES];
        }];
        /**
         *  預(yù)加載
         */
        _listTableView.js_preloadBlock = ^{
            kStrongSelf(self)
            [self requestGoodListIsReload:NO];
        };
    }
    return _listTableView;
}

至此,流程就done了

內(nèi)部方法實現(xiàn)步驟

NSObject+LoadMoreService.m

先用runtime associate property

    return [objc_getAssociatedObject(self, &key_isNoMoreData) boolValue];
}

- (void)setIsNoMoreData:(BOOL)isNoMoreData{
    objc_setAssociatedObject(self, &key_isNoMoreData, @(isNoMoreData), OBJC_ASSOCIATION_ASSIGN);
}

- (BOOL)isRequesting{
    return [objc_getAssociatedObject(self, &key_isRequesting) boolValue];
}

- (void)setIsRequesting:(BOOL)isRequesting{
    objc_setAssociatedObject(self, &key_isRequesting, @(isRequesting), OBJC_ASSOCIATION_ASSIGN);
}

- (NSInteger)currentPage{
    return [objc_getAssociatedObject(self, &key_currentPage) integerValue];
}

- (void)setCurrentPage:(NSInteger)currentPage{
    objc_setAssociatedObject(self, &key_currentPage, @(currentPage), OBJC_ASSOCIATION_ASSIGN);
}

- (NSMutableArray *)dataArray{
    return objc_getAssociatedObject(self, &key_dataArray);
}

- (void)setDataArray:(NSMutableArray *)dataArray{
    objc_setAssociatedObject(self, &key_dataArray, dataArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSMutableArray *)appendingIndexpaths{
    return objc_getAssociatedObject(self, &key_appendingIndexpaths);
}

- (void)setAppendingIndexpaths:(NSMutableArray *)appendingIndexpaths{
    objc_setAssociatedObject(self, &key_appendingIndexpaths, appendingIndexpaths, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (id)orginResponseObject{
    return objc_getAssociatedObject(self, &key_orginResponseObject);
}

- (void)setOrginResponseObject:(id)orginResponseObject{
    objc_setAssociatedObject(self, &key_orginResponseObject, orginResponseObject, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

分頁請求的base Method,
需要你配置的地方都有warning標(biāo)識著:

                               para:(NSMutableDictionary *)para
                           isReload:(BOOL)isReload{
    
    RACReplaySubject *subject = [RACReplaySubject subject];

    if (![self isSatisfyLoadMoreRequest]&&!isReload) {
        return subject;
    }
    if (!para) {
        para = [NSMutableDictionary dictionary];
    }
    if (isReload) {
        self.currentPage = 0;
#warning 此處可以添加統(tǒng)一的HUD
        //...
    }
    self.currentPage++;
#warning 分頁的key按需修改
    para[@"page"] = @(self.currentPage);
    para[@"per_page"] = @(PerPageMaxCount);
    
    self.isRequesting = YES;
    
    [[JSRequestTools js_getURL:baseURL para:para] subscribeNext:^(id  _Nullable x) {
        self.isRequesting = NO;
        if (isReload) {
#warning 消失HUD
            //...
        }
        [subject sendNext:x];
        [subject sendCompleted];
    } error:^(NSError * _Nullable error) {
        self.isRequesting = NO;
        if (self.currentPage>0) {
            self.currentPage--;
        }
        [subject sendError:error];
    }];
    
    return subject;
}

此方法統(tǒng)一處理一些操作,比如:刷新remove,轉(zhuǎn)model數(shù)組,記錄是否加載完,記錄當(dāng)前請求的indexpath數(shù)組(為了是能調(diào)用insertRowsAtIndexPath:或者是insertSections:,而不用reloadData)

                                           para:(NSMutableDictionary *)para
                                     keyOfArray:(NSString *)keyOfArray
                          classNameOfModelArray:(NSString *)classNameOfModelArray
                                       isReload:(BOOL)isReload{

    RACReplaySubject *subject = [RACReplaySubject subject];
    
    [[self js_baseSingleRequestWithURL:baseURL
                                 para:para
                             isReload:isReload] subscribeNext:^(id  _Nullable x) {
        
        NSAssert(classNameOfModelArray, @"請建個對應(yīng)的model,為了能創(chuàng)建數(shù)組模型!");
        
        self.orginResponseObject = x;
        
        if (!self.dataArray) {
            self.dataArray = @[].mutableCopy;
        }
        
        if (isReload) {
            [self.dataArray removeAllObjects];
        }
        
        NSArray *separateKeyArray = [keyOfArray componentsSeparatedByString:@"/"];
        for (NSString *sepret_key in separateKeyArray) {
            x = x[sepret_key];
        }
        
        NSArray *dataArray = [NSArray yy_modelArrayWithClass:NSClassFromString(classNameOfModelArray) json:x];
        NSInteger from_index = self.dataArray.count;
        NSInteger data_count = dataArray.count;
        self.appendingIndexpaths = [self getAppendingIndexpathsFromIndex:from_index
                                                          appendingCount:data_count
                                                               inSection:0
                                                                isForRow:YES];
        [subject sendNext:dataArray];
        
        if (dataArray.count==0) {
            self.isNoMoreData = YES;
        } else {
            self.isNoMoreData = NO;
            [self.dataArray addObjectsFromArray:dataArray];
        }
        [subject sendCompleted];
        
    } error:^(NSError * _Nullable error) {
        [subject sendError:error];
    }];
    
    return subject;
}

判斷是否滿足預(yù)加載的條件:

    return (!self.isNoMoreData&&!self.isRequesting);
}

獲取當(dāng)前分頁的所得indexpaths數(shù)組:

                                     appendingCount:(NSInteger)appendingCount
                                          inSection:(NSInteger)inSection
                                           isForRow:(BOOL)isForRow{
    NSMutableArray *indexps = [NSMutableArray array];
    for (NSInteger i = 0; i < appendingCount; i++) {
        if (isForRow) {
            NSIndexPath *indexp = [NSIndexPath indexPathForRow:from_index+i inSection:inSection];
            [indexps addObject:indexp];
        } else {
            NSIndexPath *indexp = [NSIndexPath indexPathForRow:0 inSection:from_index+i];
            [indexps addObject:indexp];
        }
    }
    return indexps;
}

UITableView+Preload.m

給tableview擴(kuò)展些屬性以及方法

統(tǒng)一給tableview設(shè)置頭部刷新

    
    MJRefreshNormalHeader *header = [MJRefreshNormalHeader headerWithRefreshingBlock:js_reloadBlock];
    self.mj_header = header;
}

結(jié)束刷新

    
    [self.mj_header endRefreshing];
}

判斷當(dāng)前index是否可以出發(fā)預(yù)加載

    NSInteger totalCount = self.dataArray.count;
    if ([self isSatisfyPreloadDataWithTotalCount:totalCount currentIndex:currentIndex]&&self.js_preloadBlock) {
        self.js_preloadBlock();
    }
}

是否達(dá)到預(yù)加載的條件

    return  ((currentIndex == totalCount - PreloadMinCount) && (currentIndex >= PreloadMinCount));
}

依賴的三方庫有:AFNetworking、ReactiveObjC、YYModel、MJRefresh

結(jié)

其實思路很簡單,runtime擴(kuò)展所需要的屬性和方法,然后有機(jī)的結(jié)合調(diào)用,如果你真的看懂了,其實真的很方便,當(dāng)然如果你有更好的建議都可以github issue我,共同學(xué)習(xí)共同進(jìn)步~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,840評論 4 61
  • 最近在做項目時發(fā)現(xiàn)列表分頁加載數(shù)據(jù)體驗并不是很好,第一個想到解決此問題的方案就是預(yù)加載,便在網(wǎng)上找了一些相關(guān)的資料...
    ControlM閱讀 2,605評論 0 4
  • 概述在iOS開發(fā)中UITableView可以說是使用最廣泛的控件,我們平時使用的軟件中到處都可以看到它的影子,類似...
    liudhkk閱讀 9,316評論 3 38
  • 對于加載數(shù)據(jù),感覺最容易上手的就是MJRefresh這個加載第三方庫了。 記得將MJRefresh導(dǎo)入工程里面,不...
    大沖哥閱讀 1,265評論 0 1
  • 我低著頭 喝著碗里的湯 說我們的過去 再抬頭時 只看到碗和散落的筷子 我追出門 你已在小道上 你用右手擦拭雙臉 卻...
    北疆牧者閱讀 518評論 1 3

友情鏈接更多精彩內(nèi)容