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 半屏適配、設置下拉樣式 https://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(獲取用户微博數據) https://download.csdn.net/download/u011018979/20689654

  • 集成下拉刷新控件:下拉刷新 HWHomeTableViewController
  • 獲取未讀消息數: HWHomeTableViewController
  • 封裝標題按鈕:HWTitleButton

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