iOS小技能:整合下拉重新整理控制元件 & 實現無感知上拉載入更多

語言: CN / TW / HK

持續創作,加速成長!這是我參與「掘金日新計劃 · 10 月更文挑戰」的第18天,點選檢視活動詳情

引言

需求:由於訊息列表,資料量比較大,為了提升使用者體驗,需採用分頁載入顯示資料

案例:iOS零售版ERP APP增加支付獎勵訊息通知

通知資訊(定時xx點;歷史訊息可查)

2021-04-29 尊敬的商家,您參與的xxx激勵金活動,昨日參與成功10筆,共獲得激勵金1元!

I 整合下/上拉重新整理控制元件

1.1 定義相關分頁屬性

  • 分頁屬性 ```objectivec

@property (nonatomic , assign) NSInteger pageNum;//當前頁碼 @property (nonatomic , assign) NSInteger pageCount;// 總頁數 @property (nonatomic , assign) BOOL isfooterRereshing; // 每頁顯示數... ```

  • VM中的事件和資料屬性

```objectivec @property (nonatomic,strong) NSMutableArray *datas;

@property (nonatomic,strong) RACSubject *reloadSubject;

@property (nonatomic,strong) RACSubject *ShowNoviewSubject;

@property (nonatomic,strong) RACSubject *hidenNoviewSubject;

```

1.2 監聽下拉和上拉事件

  • VC 監聽和處理下拉和上拉事件 ```objectivec _tableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(footerRereshing)]; _tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(headerRereshing)];

``` - 處理上拉載入資料事件

```objectivec /* 用於標誌下拉動作/ @property (nonatomic , assign) BOOL isfooterRereshing;

  • (void)footerRereshing { self.isfooterRereshing = YES;

    if ((_pageNum + 1) > _pageCount) {

    [self.tableView.mj_footer endRefreshingWithNoMoreData];
    
    return;
    

    }

    _pageNum = _pageNum + 1; [self doorRequest];

}

- 處理下拉重新整理資料事件objectivec - (void)headerRereshing { self.isfooterRereshing = NO;

[_doorArr removeAllObjects];// 移除資料,可請求成功之後,再移除
_pageNum = 1;
[self doorRequest];

}

```

1.3 請求資料的處理

請求成功和失敗都要關閉重新整理檢視 ```objectivec [weakSelf.vcView.tableView.mj_footer endRefreshing]; [weakSelf.vcView.tableView.mj_header endRefreshing];

完成處理的程式碼objectivec - (void)doorRequest {

//暫無資料

if (self.viewModel.datas.count == 0) {
    [self.viewModel.ShowNoviewSubject sendNext:QCTLocal(@"no_data")];

}else{
    [self.viewModel.hidenNoviewSubject sendNext:QCTLocal(@"")];

}

if(![UserInfoModel.shareUserInfoModel ispayStoreId]){

    //        [self showHUDMessage:@"請先進件"];
    // 顯示暫無資料

    return;

}


NSString *post = [NSString stringWithFormat:@"%@%@",currentPayHost,@""];



NSMutableDictionary *params = [NSMutableDictionary dictionary];
//111850
//    [params setValue:@"" forKey:@"sid"];
[params setValue:UserInfoModel.shareUserInfoModel.store.payStoreId forKey:@"sid"];

[params setValue:[[NSNumber numberWithInteger:self.pageNum]description] forKey:@"page"];


[params setValue:kPageSize forKey:@"pageSize"];




__weak __typeof__(self) weakSelf = self;

[QCTNetworkHelper Post:post parameters:params success:^(NSDictionary* responseObj) {


    NSDictionary *data = nil;

    if([responseObj.allKeys containsObject:@"data"]){



        data = responseObj[@"data"];




    }else{

        [self showHUDMessage:@"資料異常!"];
        //                    [SVProgressHUD showInfoWithStatus:@"資料異常!"];

        return;// 獲取資料失敗

    }

    if([data.allKeys containsObject:@"data"]){


        data = responseObj[@"data"];

    }else{
        //                    [SVProgressHUD showInfoWithStatus:@"資料異常!"];
        [self showHUDMessage:@"資料異常!"];

        return;// 獲取資料失敗


    }


    NSMutableArray* tmparrresult = [ERPTradeRewardReportDto mj_objectArrayWithKeyValuesArray:data[@"data"]];

    if(self.isfooterRereshing){

        [weakSelf.viewModel.datas addObjectsFromArray:tmparrresult];

    }else{

        weakSelf.viewModel.datas  =  tmparrresult ;

    }


    [weakSelf.vcView.tableView reloadData];


    weakSelf.pageCount = [responseObj[@"data"][@"pageCount"] integerValue];
    weakSelf.pageNum = [responseObj[@"data"][@"page"] integerValue];






    [weakSelf.vcView.tableView.mj_footer endRefreshing];
    [weakSelf.vcView.tableView.mj_header endRefreshing];




    if (weakSelf.viewModel.datas.count == 0) {
        [weakSelf.viewModel.ShowNoviewSubject sendNext:QCTLocal(@"no_data")];


    }else{
        [weakSelf.viewModel.hidenNoviewSubject sendNext:QCTLocal(@"no_data")];

    }



} failure:^(NSError * _Nonnull error) {

    [QCTNetworkHelper   showLoading_failed_please_try_again_laterBlock];
    [self.vcView.tableView.mj_footer endRefreshing];
    [self.vcView.tableView.mj_header endRefreshing];

} bizFailure:^(id  _Nonnull responseObj) {


    [self.vcView.tableView.mj_footer endRefreshing];
    [self.vcView.tableView.mj_header endRefreshing];


    [QCTNetworkHelper showresponseObjmessage:responseObj];



}  isShowLoadingDataGif:YES];

}

```

II iOS實現無感知上拉載入更多

2.1 思路1:UITableViewDataSourcePrefetching

```objectivec // this protocol can provide information about cells before they are displayed on screen.

@protocol UITableViewDataSourcePrefetching

@required

// indexPaths are ordered ascending by geometric distance from the table view - (void)tableView:(UITableView )tableView prefetchRowsAtIndexPaths:(NSArray )indexPaths;

@optional

// indexPaths that previously were considered as candidates for pre-fetching, but were not actually used; may be a subset of the previous call to -tableView:prefetchRowsAtIndexPaths: - (void)tableView:(UITableView )tableView cancelPrefetchingForRowsAtIndexPaths:(NSArray )indexPaths;

@end

```

2.2 實現思路2:通過 KVO 去監聽 scrollView 的 contentOffset 變化

MJRefreshAutoFooter 有個專門的屬性triggerAutomaticallyRefreshPercent 去做自動重新整理

```objectivec

import "MJRefreshFooter.h"

NS_ASSUME_NONNULL_BEGIN

@interface MJRefreshAutoFooter : MJRefreshFooter /* 是否自動重新整理(預設為YES) / @property (assign, nonatomic, getter=isAutomaticallyRefresh) BOOL automaticallyRefresh;

/* 當底部控制元件出現多少時就自動重新整理(預設為1.0,也就是底部控制元件完全出現時,才會自動重新整理) / @property (assign, nonatomic) CGFloat appearencePercentTriggerAutoRefresh MJRefreshDeprecated("請使用triggerAutomaticallyRefreshPercent屬性");

/* 當底部控制元件出現多少時就自動重新整理(預設為1.0,也就是底部控制元件完全出現時,才會自動重新整理) / @property (assign, nonatomic) CGFloat triggerAutomaticallyRefreshPercent;

/** 自動觸發次數, 預設為 1, 僅在拖拽 ScrollView 時才生效,

如果為 -1, 則為無限觸發 */ @property (nonatomic) NSInteger autoTriggerTimes; @end

```

III 重新整理控制元件的適配

  1. 上拉載入:安全區域距離適配 ```objectivec

define k_safeAreaInsetsBottom [UIApplication sharedApplication].delegate.window.safeAreaInsets.bottom

define isIphoneX isHasSafeAreaInsets

define k_ignoredScrollViewContentInsetBottom (isIphoneX?k_safeAreaInsetsBottom:0)

    _vcView.tableView.mj_footer.ignoredScrollViewContentInsetBottom = k_ignoredScrollViewContentInsetBottom;

```

  1. 下拉重新整理適配:present 半屏適配、設定下拉樣式 http://blog.csdn.net/z929118967/article/details/104477314

  2. 分頁併發適配: 方式1. 升級MJRefresh到3.7.5版本 Fix/duplicated async method -> Installing MJRefresh 3.7.5 (was 3.3.1) 方式2. 使用自動重新整理控制元件MJRefreshNormalHeader->MJRefreshAutoNormalFooter

see also

案例:新浪微博API(獲取使用者微博資料) http://download.csdn.net/download/u011018979/20689654

  • 整合下拉重新整理控制元件:下拉重新整理 HWHomeTableViewController
  • 獲取未讀訊息數: HWHomeTableViewController
  • 封裝標題按鈕:HWTitleButton

更多內容請關注公眾號:iOS逆向