你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> 利用預加載讓分頁加載不再繁瑣之單個分頁講解

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

編輯:IOS開發基礎

原文

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

大概就是這麼絲滑

yun_pdlist_preload.gif

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

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved