转载

利用预加载让分页加载不再繁琐之单个分页讲解

原文

大概是项目里太多的分页加载数据,所以一个简单、快捷、高效分页加载会使你那么的愉悦.

大概就是这么丝滑

利用预加载让分页加载不再繁琐之单个分页讲解

github链接:JSLoadMoreService

用法讲解

属性预览

NSObject+LoadMoreService.h/**

 *  分页请求数量  */ static NSInteger const PerPageMaxCount = 20; @interface NSObject (LoadMoreService) /**  *  每次请求追加的indexpaths  */ @property (nonatomic, strong) NSMutableArray *appendingIndexpaths; /**  *  数据数组  */ @property (nonatomic, strong) NSMutableArray *dataArray; /**  *  原始请求数据  */ @property (nonatomic, strong) id orginResponseObject; /**  *  当前页码  */ @property (nonatomic, assign) NSInteger currentPage; /**  *  是否请求中  */ @property (nonatomic, assign) BOOL isRequesting; /**  *  是否数据加载完  */ @property (nonatomic, assign) BOOL isNoMoreData; /**  *  单一请求分页加载数据  *  *  @param baseURL               请求地址  *  @param para                  请求参数  *  @param keyOfArray            取数组的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

/**  *  预加载触发的数量  */ static NSInteger const PreloadMinCount = 10; typedef void(^PreloadBlock)(void); typedef void(^ReloadBlock)(void); @interface UITableView (Prereload) /**  *  预加载回调  */ @property (nonatomic, copy  ) PreloadBlock js_preloadBlock; /**  *  tableview数据  */ @property (nonatomic, strong) NSMutableArray *dataArray; /**  *  计算当前index是否达到预加载条件并回调  *  *  @param currentIndex row or section  */ - (void)preloadDataWithCurrentIndex:(NSInteger)currentIndex; /**  *  上拉刷新  *  *  @param js_reloadBlock 刷新回调  */ - (void)headerReloadBlock:(ReloadBlock)js_reloadBlock; /**  *  结束上拉刷新  */ - (void)endReload;

如何调用

建一个viewModel类

这里处理数据的逻辑,所以写了方法

(RACSignal *)siganlForJokeDataIsReload:(BOOL)isReload ``` 下面就是怎样调用分类的方法: ```- (RACSignal *)siganlForJokeDataIsReload:(BOOL)isReload{          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 传过来的数组          *  你可以对每次传过来的数组的元素"再加工",知道达到你的要求后 再 sendNext          */         //...         [subject sendNext:x];     } error:^(NSError * _Nullable error) {         [subject sendError:error];     } completed:^{         /**          *  走到这里为,每次分页请求所有逻辑处理完毕          */         [subject sendCompleted];     }];          return subject; }

VC调用:

整个方法:

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

tableview里调用预加载

绘制cell代理里调用,根据你的需求是row or section

(void)configureCell:(UITableViewCell *)cell     forRowAtIndexPath:(NSIndexPath *)indexPath {     JSGoodListModel *model = self.dataArray[indexPath.row];     cell.textLabel.text = model.title;     cell.detailTextLabel.text = [NSString stringWithFormat:@"?%@",model.price];     /**      *  根据当期index计算是否回调preloadblock      */     [self preloadDataWithCurrentIndex:indexPath.row]; }

配置tableview的上拉刷新和预加载:

- (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];         }];         /**          *  预加载          */         _listTableView.js_preloadBlock = ^{             kStrongSelf(self)             [self requestGoodListIsReload:NO];         };     }     return _listTableView; }

至此,流程就done了

内部方法实现步骤

NSObject+LoadMoreService.m

先用runtime associate property

(BOOL)isNoMoreData{     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标识着:

(RACSignal *)js_baseSingleRequestWithURL:(NSString *)baseURL                                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 此处可以添加统一的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];     }];     1     return subject; }

此方法统一处理一些操作,比如:刷新remove,转model数组,记录是否加载完,记录当前请求的indexpath数组(为了是能调用insertRowsAtIndexPath:或者是insertSections:,而不用reloadData)

(RACSignal *)js_singalForSingleRequestWithURL:(NSString *)baseURL                                            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, @"请建个对应的model,为了能创建数组模型!");                  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; }

判断是否满足预加载的条件:

(BOOL)isSatisfyLoadMoreRequest{     return (!self.isNoMoreData&&!self.isRequesting); }

获取当前分页的所得indexpaths数组:

(NSMutableArray *)getAppendingIndexpathsFromIndex:(NSInteger)from_index                                      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扩展些属性以及方法

统一给tableview设置头部刷新

(void)headerReloadBlock:(ReloadBlock)js_reloadBlock{     MJRefreshNormalHeader *header = [MJRefreshNormalHeader headerWithRefreshingBlock:js_reloadBlock];     self.mj_header = header; }

结束刷新

(void)endReload{        [self.mj_header endRefreshing]; }

判断当前index是否可以出发预加载

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

是否达到预加载的条件

(BOOL)isSatisfyPreloadDataWithTotalCount:(NSInteger)totalCount currentIndex:(NSInteger)currentIndex{     return  ((currentIndex == totalCount - PreloadMinCount) && (currentIndex >= PreloadMinCount)); }

依赖的三方库有:AFNetworking、ReactiveObjC、YYModel、MJRefresh

其实思路很简单,runtime扩展所需要的属性和方法,然后有机的结合调用,如果你真的看懂了,其实真的很方便,当然如果你有更好的建议都可以github issue我,共同学习共同进步~

正文到此结束
Loading...