原文
大概是項目裡太多的分頁加載數據,所以一個簡單、快捷、高效分頁加載會使你那麼的愉悅.
大概就是這麼絲滑

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我,共同學習共同進步~