對(duì)于普通的App來講一般都會(huì)存在列表界面又厉,而對(duì)于列表界面來說下拉刷新和上拉加載又是必不可少的,其實(shí)這些我們都會(huì)用的到欣孤,所以想把代碼中用到的地方梳理下馋没。以普通的首頁列表頁面為例子昔逗,刷新就以iOS中使用最為廣泛的MJRefresh為例子講解降传。
一、定義網(wǎng)絡(luò)請(qǐng)求中不同的刷新狀態(tài)
typedef NS_ENUM(NSInteger, LoadingType) {
LoadingTypeNormal,//正常加載
LoadingTypeRefresh,//下拉刷新
LoadingTypeLoadMore//上拉加載
};
一般網(wǎng)絡(luò)請(qǐng)求 我是放在 viewModel中去處理了勾怒,每個(gè)模塊對(duì)應(yīng)一個(gè)viewModel婆排,這個(gè)viewModel 繼承于BaseViewModel声旺,所以上邊的枚舉 也在BaseViewMdoel中定義,方便子類的使用段只。 LoadingTypeNormal,//正常加載 這個(gè)狀態(tài)一般適用于 首次進(jìn)入列表頁面 沒有下拉刷新的情況下直接請(qǐng)求數(shù)據(jù)
二腮猖、定義各種刷新完成后的狀態(tài)和網(wǎng)絡(luò)請(qǐng)求方法
在.h文件中一般需要定義這些屬性
@property (nonatomic, assign) LoadingType loadingType;// 刷新狀態(tài)
@property (nonatomic, strong) NSError *error; //error信息
@property (nonatomic, assign) BOOL isListDataCompleted;//首次加載完成
@property (nonatomic, assign) BOOL isPullRefreshComplted;//下拉刷新加載完成
@property (nonatomic, assign) BOOL isLoadMoreComplted;// 上拉加載
@property (nonatomic, assign) BOOL isResetNoMoreData;// 上拉加載沒有更多數(shù)據(jù)
@property (nonatomic, strong) NSMutableArray *dataArray;//返回的 數(shù)據(jù)的數(shù)組
通過BOOL值標(biāo)記加載是否完成的狀態(tài),一般會(huì)分為首次加載完成赞枕、下拉刷新加載完成澈缺、上拉加載完成、上拉加載無更多數(shù)據(jù)然后通過RAC 或者 block將值進(jìn)行回傳給viewController ,viewController 通過返回的狀態(tài)進(jìn)行 數(shù)據(jù)的刷新或者其它的操作
一般請(qǐng)求的方法傳入這兩個(gè)參數(shù)就夠了
- (void)requestWithParams:(NSDictionary *)params loadingType:(LoadingType)loadingType;
一個(gè)是 包含請(qǐng)求數(shù)據(jù)的Dictionary 炕婶,一個(gè)是請(qǐng)求的狀態(tài)姐赡,因?yàn)橹灰@兩個(gè)就夠了,可能你會(huì)問 加載的page 和pageCount 不是還需要viewController處理嗎柠掂,是的 下拉刷新和上拉加載肯定免不了對(duì)page和pageCount 處理项滑,但是不需要在viewController 中 而是在viewModel中
@interface HomeViewModel()
@property (nonatomic, assign) NSInteger pageCount;
@property (nonatomic, assign) NSInteger page;
@end
- (instancetype)init {
self = [super init];
if (self) {
self.pageCount = 20;
self.page = 1;
}
return self;
}
在 viewModel的.m文件中定義和初始化 page和pageCount,因?yàn)樗⑿潞图虞d的數(shù)據(jù)處理都在viewModel中涯贞,viewController只關(guān)心數(shù)據(jù)源dataArray和刷新加載完成的狀態(tài)所以在這里處理page和pageCount
三枪狂、處理請(qǐng)求邏輯
- (void)requestWithParams:(NSDictionary *)params loadingType:(LoadingType)loadingType {
self.loadingType = loadingType;
NSMutableDictionary *paramsDic = [NSMutableDictionary dictionaryWithDictionary:self.baseParams];
[paramsDic addEntriesFromDictionary:self.userParams];
[paramsDic addEntriesFromDictionary:self.baseParams];
if (self.loadingType == LoadingTypeNormal || self.loadingType == LoadingTypeRefresh) {
self.page = 1;
[paramsDic setObject:@(self.page) forKey:@"page"];
} else {
[paramsDic setObject:@(self.page + 1) forKey:@"page"];
}
[paramsDic setObject:@(self.pageCount) forKey:@"page_count"];
BOOL isNeedLoading = self.loadingType == LoadingTypeNormal ? YES : NO;
[[LLNetwork shareInstance] getWithURL:@"APIManager.home"
token:nil
params:paramsDic
isLoading:isNeedLoading
success:^(id response) {
self.response = response;
[self handleDataWithResponse:response];
} failure:^(NSError *error) {
self.error = error;
}];
}
這里關(guān)鍵的點(diǎn)在于 page 和pageCount參數(shù)的處理,在正常請(qǐng)求加載的情況下宋渔,需要將
self.page = 1;
因?yàn)榇藭r(shí)請(qǐng)求的一般是首頁數(shù)據(jù) page就傳1
當(dāng)為上拉加載的時(shí)候 為什么
[paramsDic setObject:@(self.page + 1) forKey:@"page"];
而不是
[paramsDic setObject:@(self.page ++) forKey:@"page"];
咋一看 這樣處理毫無差別 州疾,都是傳遞的page 加 1,其實(shí)下邊的處理是不妥的傻谁,因?yàn)樯侠虞d只能是 加載成功了 page 才能進(jìn)行++的孝治,試想這樣的case 網(wǎng)絡(luò)不好 我正在加載第2頁,上拉加載一直加載失敗 我如果self.page++ 那么 當(dāng)網(wǎng)絡(luò)恢復(fù)良好的時(shí)候 此時(shí)self.page不知道是多少了审磁,也不是我想要的第二頁了谈飒,所以此時(shí)不改變 self.page 的值 只是將傳遞的參數(shù) 為
self.page + 1 上拉加載成功后在進(jìn)行 self.page++ ,這樣做保證了self.page 值得正確性
self.loadingType = loadingType;
注意每次請(qǐng)求都要更新下現(xiàn)在的請(qǐng)求狀態(tài),因?yàn)楹罄m(xù)的數(shù)據(jù)處理需要依賴這個(gè)狀態(tài)
四态蒂、處理接受數(shù)據(jù)后的邏輯
對(duì)于數(shù)據(jù)成功后的處理
- (void)handleDataWithResponse:(id)response {
DebugLog(@"首頁數(shù)據(jù)=%@",response);
BOOL isSuccess = [response[@"is_success"] boolValue];
if (!isSuccess) {
NSString *errorMessage = response[@"error_msg"];
DebugLog(errorMessage)
return;
}
NSDictionary *dataDic = response[@"data"];
NSArray *producrsArray = dataDic[@"product_list"];
NSArray *listArray = [HomeModel mj_objectArrayWithKeyValuesArray:producrsArray];
NSMutableArray *tempDataArray = [NSMutableArray arrayWithArray:listArray];
if (tempDataArray.count < self.pageCount) {
self.isResetNoMoreData = YES;
} else {
self.isResetNoMoreData = NO;
}
if (self.loadingType == LoadingTypeNormal) {//首次加載
self.dataArray = tempDataArray;
self.isListDataCompleted = YES;
} else if (self.loadingType == LoadingTypeRefresh) {//下拉刷新
self.dataArray = tempDataArray;
self.isPullRefreshComplted = YES;
} else if (self.loadingType == LoadingTypeLoadMore) {//上拉加載
[self.dataArray addObjectsFromArray:tempDataArray];
self.isLoadMoreComplted = YES;
self.page++;
}
}
每次請(qǐng)求結(jié)束 對(duì)于是否有更多數(shù)據(jù)的判斷是 當(dāng)次請(qǐng)求的數(shù)據(jù)條數(shù)是否小于pageCount杭措,如果小于則判斷為最后一頁 ,再次上拉加載需要提示 " 沒有更多數(shù)據(jù)"
if (tempDataArray.count < self.pageCount) {
self.isResetNoMoreData = YES;
} else {
self.isResetNoMoreData = NO;
}
四钾恢、viewModel和viewController交互邏輯處理
前邊提到的
@property (nonatomic, assign) BOOL isListDataCompleted;//首次加載完成
@property (nonatomic, assign) BOOL isPullRefreshComplted;//下拉刷新加載完成
@property (nonatomic, assign) BOOL isLoadMoreComplted;// 上拉加載
@property (nonatomic, assign) BOOL isResetNoMoreData;// 上拉加載沒有更多數(shù)據(jù)
這些狀態(tài) viewController是需要知道的 手素,因?yàn)?loading框的顯示隱藏、刷新菊花的顯示與停止都需要這些狀態(tài)的判斷才能做出下一步的操作瘩蚪,所以我們還是需要將這些狀態(tài)進(jìn)行回傳泉懦,當(dāng)然回傳值 的方法有很多,delegate疹瘦、block等崩哩,筆者偏向于 使用RAC 這樣代碼比較簡(jiǎn)略
在viewController的代碼入下
#import "HomeViewController.h"
#import "HomeViewModel.h"
@interface HomeViewController ()
@property (nonatomic, strong) HomeViewModel *mainViewModel;
@end
@implementation HomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self add_Observer];
}
#pragma mark Priviate - methods
- (void)add_Observer {
@weakify(self);
[[RACObserve(self.mainViewModel, error)ignore:nil]subscribeNext:^(NSError *error) {
@strongify(self);
DebugLog(@"%ld, %@", error.code , error.description);
[self handleErrorInfo:error];
}];
[[RACObserve(self.mainViewModel, fixedFilterModel)ignore:nil]subscribeNext:^(FixedFilterModel *model) {
@strongify(self);
self.fixedFilterModel = model;
}];
[[RACObserve(self.mainViewModel, isListDataCompleted)ignore:nil]subscribeNext:^(NSNumber *value) {
@strongify(self);
if ([value integerValue]) {
[self hideSeverErrorView];
[self hideNetworkUnReachableView];
[self.listView reloadData];
if (self.mainViewModel.isResetNoMoreData) {
[self.listView.listTableView footerEndRefreshingWithNoMoreData];//footer 停止加載顯示 無更多數(shù)據(jù)
} else {
[self.listView.listTableView footerEndRefreshing];//footer 僅僅停止加載
}
}
}];
//下拉完成
[[RACObserve(self.mainViewModel, isPullRefreshComplted)ignore:nil]subscribeNext:^(NSNumber *value) {
@strongify(self);
if ([value integerValue]) {
[self.listView.listTableView headerEndRefreshing];
[self.listView reloadData];
if (self.mainViewModel.isResetNoMoreData) { //footer 停止加載顯示 無更多數(shù)據(jù)
[self.listView.listTableView footerEndRefreshingWithNoMoreData];
} else {
[self.listView.listTableView footerEndRefreshing];//footer 僅僅停止加載
}
}
}];
// 上拉加載
[[RACObserve(self.mainViewModel, isLoadMoreComplted)ignore:nil]subscribeNext:^(NSNumber *value) {
@strongify(self);
if ([value integerValue]) {
[self.listView reloadData];
if (self.mainViewModel.isResetNoMoreData) {
[self.listView.listTableView footerEndRefreshingWithNoMoreData];//footer 停止加載顯示 無更多數(shù)據(jù)
} else {
[self.listView.listTableView footerEndRefreshing]; // footer 僅僅停止加載
}
}
}];
}
- (HomeViewModel *)mainViewModel {
if (!_mainViewModel) {
_mainViewModel = [[HomeViewModel alloc] init];
}
return _mainViewModel;
}
@end
從代碼邏輯也能看到,viewController作用主要為獲取各種狀態(tài)然后處理這些狀態(tài)和進(jìn)行數(shù)據(jù)的刷新加載
需要注意的點(diǎn)是在 正常加載和下拉刷新的時(shí)候也需要判斷 數(shù)據(jù)是否加載完成了 ,因?yàn)榧偃?pageCount == 10 的情況下 如果只有5條數(shù)據(jù)那么 首次加載后 根本不需要下拉刷新和上拉加載就已經(jīng)加載完了數(shù)據(jù)邓嘹,此時(shí)就要更新footer顯示無更多數(shù)據(jù)了酣栈。
上邊就是簡(jiǎn)單的對(duì)于下拉刷新和上拉加載 自己的一點(diǎn)見解,在項(xiàng)目中也是這樣用的汹押,感覺不錯(cuò)的地方做個(gè)記錄矿筝,歡迎各位批評(píng)指正。
代碼地址 :https://git.coding.net/liwb/UserProject.git