相信大家都有遇到在項目中遇到過關(guān)于 UITableView 或 UICollectionView 實現(xiàn)下的占位圖需求芍躏。
比如某魚 App 中對于各種不同場景需求顯示的占位圖:
- 數(shù)據(jù)加載中的占位圖
- 網(wǎng)絡(luò)異常時的占位圖
- 無數(shù)據(jù)時的占位圖
思考#
那么疑問來了,遇到這種需求時你會如何進(jìn)行相關(guān)實現(xiàn)呢?
創(chuàng)建三種不同的占位圖腰涧,然后再根據(jù)不同的場景調(diào)整與之相關(guān)的**UITableVIew ** 視圖層級來進(jìn)行顯示槽唾?(當(dāng)然這里還不包括在不同場景下交互邏輯姐叁,占位圖是否可點擊操作航背?占位圖顯示時 UITableView 滾動限制等等)
可能也有稍微靈活一些的方案,依舊創(chuàng)建三種不同的占位圖巫击,但是在管理視圖顯示邏輯時根據(jù)不同場景情況下將與 UITableView 大小相等的占位圖加載至或替換原有的 **UITableVIewHeaderView/TableViewFooterView ** 來進(jìn)行實現(xiàn)榆芦,這樣實現(xiàn)的好處避免了多種占位圖以及多種場景下的層級顯示處理邏輯,同時也能很好的適配各種不同 UITableView 的大小并且在相關(guān)滾動限制上能很好的進(jìn)行掌控喘鸟。
......
小編以前有幸看到了某一家公司關(guān)于這方面的需求實現(xiàn)匆绣,在看了一部分源碼之后我差點就當(dāng)場原地自爆,除了復(fù)雜的多場景占位圖 Frame 變更之外還有與之附帶的多頁面代碼入侵什黑,不得不佩服當(dāng)時寫下這些代碼的人的腦力與體力崎淳,但是秉承偷懶才是 IT 行業(yè)的第一生產(chǎn)力的名言,所以小編在參考了其它一些大牛的實現(xiàn)思路后愕把,決定擼一個屬于自己的小框架拣凹。
![OJ11D}6~K(J`7]0)PSC]7JC.gif](http://upload-images.jianshu.io/upload_images/3831145-c5b34482f3bb68a4.gif?imageMogr2/auto-orient/strip)
使用方式及相關(guān)介紹#
該框架提供了三種不同場景下的占位圖顯示功能(UICollectionView 版本請參考以下 UITableVIew 實現(xiàn)流程)
1. 請先在你所使用的 UITableView 或者 UITableViewConroller 內(nèi)部實現(xiàn) WCQTableViewPlaceholderDelegate 代理方法
返回加載時的默認(rèn)占位圖方法(@required)
- (UIView *)wcq_tableViewPlaceholderInLoadingState;
返回網(wǎng)絡(luò)異常時的默認(rèn)占位圖方法(@required)
- (UIView *)wcq_tableViewPlaceholderInAnormalNetState;
返回空數(shù)據(jù)時的默認(rèn)占位圖方法(@required)
- (UIView *)wcq_tableViewPlaceholderInEmptyDatasourceState;
控制默認(rèn)占位圖顯示時 UITableView 的滾動限制(@optional)
- (BOOL)wcq_enableScroll;
2. 框架內(nèi)部實現(xiàn)了關(guān)于 UITableView 的一個分類(WCQTableViewPlaceholder),該分類為 UITableView 提供了:
- 網(wǎng)絡(luò)異常時自動加載相關(guān)默認(rèn)占位圖的功能
- (void)wcq_setAnormalNetwork;
- 數(shù)據(jù)加載時自動加載相關(guān)默認(rèn)占位圖的功能
- (void)wcq_setLoadingState;
- 而空數(shù)據(jù)時自動加載相關(guān)默認(rèn)占位圖的實現(xiàn)則是基于 UITableView 數(shù)據(jù)源驅(qū)動實現(xiàn)的,使用者在使用系統(tǒng) ReloadData 方法時框架會自行判斷加載恨豁,無需使用者添加相關(guān)代碼
代碼示例#
@interface ViewController ()<UITableViewDelegate, UITableViewDataSource, WCQTableViewPlaceholderDelegate>
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) NSMutableArray *dataArray;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self.view addSubview:self.tableView];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Refresh Methods
- (void)reloadData {
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:@"http://baike.baidu.com/api/openapi/BaikeLemmaCardApi?scope=103&format=json&appid=379020&bk_key=hello&bk_length=600"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
[self.tableView wcq_setLoadingState];
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(@"Error: %@", error);
[self.tableView.mj_header endRefreshing];
[self.tableView wcq_setAnormalNetwork];
} else {
NSLog(@"%@ %@", response, responseObject);
[self.tableView.mj_header endRefreshing];
self.dataArray = [NSMutableArray arrayWithArray:@[@"1", @"2", @"3"]];
[self.tableView reloadData];
}
}];
[dataTask resume];
}
- (void)loadData {
[self.tableView wcq_setLoadingState];
[self.dataArray removeAllObjects];
[self.tableView performSelector:@selector(reloadData) withObject:nil afterDelay:3.0];
[self.tableView.mj_footer endRefreshing];
}
#pragma mark - UITableViewDataSource Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.dataArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([UITableViewCell class])];
cell.backgroundColor = [UIColor colorWithRed:arc4random()%256/255.0
green:arc4random()%256/255.0
blue:arc4random()%256/255.0
alpha:1];
return cell;
}
#pragma mark - UITableViewDelegate Methods
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 100;
}
#pragma mark - WCQTableViewPlaceholderDelegate Methods
- (UIView *)wcq_tableViewPlaceholderInLoadingState {
UIImageView *placeholderView = [UIImageView new];
placeholderView.contentMode = UIViewContentModeScaleAspectFill;
placeholderView.image = [UIImage imageNamed:@"loading"];
return placeholderView;
}
- (UIView *)wcq_tableViewPlaceholderInAnormalNetState {
UIImageView *placeholderView = [UIImageView new];
placeholderView.contentMode = UIViewContentModeScaleAspectFill;
placeholderView.image = [UIImage imageNamed:@"anormal"];
return placeholderView;
}
- (UIView *)wcq_tableViewPlaceholderInEmptyDatasourceState {
UIImageView *placeholderView = [UIImageView new];
placeholderView.contentMode = UIViewContentModeScaleAspectFill;
placeholderView.image = [UIImage imageNamed:@"emptyData"];
return placeholderView;
}
- (BOOL)wcq_enableScroll {
return YES;
}
#pragma mark - Getter Methods
- (UITableView *)tableView {
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
_tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(reloadData)];
_tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadData)];
_tableView.dataSource = self;
_tableView.delegate = self;
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
[_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:NSStringFromClass([UITableViewCell class])];
}
return _tableView;
}
- (NSMutableArray *)dataArray {
if (!_dataArray) {
_dataArray = [[NSMutableArray alloc] init];
}
return _dataArray;
}
@end
效果圖:#
注意####
下啦刷新是真正進(jìn)行網(wǎng)絡(luò)請求操作嚣镜,而上啦刷新則是模擬網(wǎng)絡(luò)請求失敗,并未進(jìn)行網(wǎng)絡(luò)請求操作橘蜜,請同學(xué)們注意
Demo 中所有占位圖均為圖片菊匿,如網(wǎng)絡(luò)異常占位圖付呕,圖中按鈕無法進(jìn)行相關(guān)點擊操作,如果有這方面交互需求的同學(xué)跌捆,可以在相對應(yīng)的返回默認(rèn)占位圖代理方法中實現(xiàn)徽职。
總結(jié)#
- 對原有代碼侵入性小,建議大家可以自定義一個統(tǒng)一的 UITableView 或者 UITableViewController 進(jìn)行相關(guān)管理
- 簡單易用同時降低開發(fā)者對于后期維護(hù)的成本佩厚,而且開放多種不同場景的默認(rèn)占位圖顯示接口
WCQTableViewPlaceholder地址(內(nèi)附Demo)
廣告#
本人也是新手入門如果發(fā)現(xiàn)本文有什么錯誤的地方可以下方留言跟我反饋姆钉,哪怕是文章的排版問題還是其他影響到閱讀體驗的問題都可以反饋我,大家一起探討如果我確實寫的有問題我會欣然接受并改正抄瓦。同時我的新浪微博是 KeepMoveingOn潮瓶,有任何問題也都可以@我,我會盡快回復(fù)大家的钙姊,感謝各位看官花費寶貴的時間閱讀此文??