关于iOS数据加载的方法,网上可以找到许多方法,其中也不乏优秀的开源项目,但就使用而言,还是有些难以达到事半功倍的效果,所以我决定编写一个足够简单、效率更高的开源库。
KWListView用于iOS开发中列表数据的加载,旨在让列表加载及显示变得简单容易。
注:采用自动高度计算功能
导入文件,文件结构如下图所示:
关于 listView 的一些配置信息都在 KWListViewConfig.m 文件中保存
| 参数 | 说明 | |-------------------------------------------------------| | KWRefreshFooterStateNoMoreDataText | 没有更多数据 | | KWListViewFailedText | 数据加载失败 | | KWListViewParamPage | 网络请求中的请求页码 | | KWListViewParamPageSize | 网络请求中请求数据量 |
导入头文件,与 UITableView 一样创建,设置代理和数据源,设置的分别是 KWListViewDelegate 和 KWListViewDataSource,而不是 UITableViewDelegate 和 UITableViewDataSource
KWListView *listView = [[KWListView alloc] initWithFrame:self.view.bounds];
listView.delegate = self;
// 等同于 listView.listDelegate = self;
listView.dataSource = self;
// 等同于 listView.listDataSource = self;
在配置文件中,我们可以配置加载失败和加载不到数据的显示文字及图标,对于需要显示不同内容的个别列表页面,我们可以在 ListView 的方法中,如下:
[listView setEmptyText:@"没有加载到数据哟~~" image:nil];
[listView setFailedText:@"无法连接到KWListView服务器" image:nil];
** 重点 ** 具体说明都写在代码注释中
__weak __typeof(self) weakSelf = self;
listView.requestAction = ^(NSDictionary *parames,void(^done)(BOOL , NSArray *)){
/*
* parames 中为请求所用的分页信息
* 包括 page、page_size,如果与您需要的不相符,请在配置中修改
* KWListViewParamPage 、 KWListViewParamPageSize
* bloack中一般写网络请求
* 网络请求参数请在parames基础上添加. 例:
* NSMutableDictionary *newParames = [parames mutableCopy];
* newParames[@"id"] = @"1";
*/
__strong __typeof(self) safeSelf = weakSelf;
// done的两个参数传送网络请求返回的成功/失败及数据,以下仅为模拟
if (safeSelf.type == PlayTypeEmpty) {
done(YES,@[]);
} else if (safeSelf.type == PlayTypeFail) {
done(NO,@[]);
} else {
NSInteger loc = ([parames[KWListViewParamPage] integerValue] - 1) * [parames[KWListViewParamPageSize] integerValue];
// 以下这段代码除了 done() ,都只是在模拟分页数据,真实请求数据不用这样
NSInteger len = [parames[KWListViewParamPageSize] integerValue];
if (len > safeSelf.successData.count - loc) {
len = safeSelf.successData.count - loc;
}
NSRange range = NSMakeRange(loc, len);
NSLog(@"%@",NSStringFromRange(range));
done(YES,[safeSelf.successData subarrayWithRange:range]);
}
};
以上操作执行后,将 listView 添加到视图中,并调用以下方法刷新界面。
[listView loadData];
接下来就是实现数据源方法了。那么我们的 KWListView 只需实现一个数据源方法即可显示数据,哪个呢?如下:
- (UITableViewCell *)listView:(KWListView *)listView cellForRow:(NSInteger)row;
在系统的 UITableView 中,我们需要至少实现两个数据源方法才能显示数据,这两个方法无非是 numberOfRows 和 cellForRow
在这里,我们只需要告诉它加载哪种 cell 就可以了,建议使用工厂方法来创建 cell,并实现复用。以下是我刚刚描述的这些问题的示例
- (UITableViewCell *)listView:(KWListView *)listView cellForRow:(NSInteger)row
{
PlaySuccessModel *model = listView.datas[row];
return [PlayViewCell cellWithTableView:listView model:model];
}
// PlayViewCell.m
+ (instancetype)cellWithTableView:(UITableView *)tableView model:(PlaySuccessModel *)model
{
static NSString *ID = @"PlayViewCell";
PlayViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (!cell) {
cell = [[PlayViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
[cell addBaseView];
}
cell.model = model;
return cell;
}
- (void)addBaseView
{
UILabel *nameLabel = [[UILabel alloc] init];
nameLabel.font = [UIFont systemFontOfSize:14];
nameLabel.frame = CGRectMake(10, 10, 200, [nameLabel.font singleLineHeight]);
nameLabel.textColor = [UIColor redColor];
[self.contentView addSubview:nameLabel];
self.nameLabel = nameLabel;
UILabel *contentLabel = [[UILabel alloc] initWithFrame:CGRectMake(nameLabel.frame.origin.x, nameLabel.frame.size.height + nameLabel.frame.origin.y + 10, [UIScreen mainScreen].bounds.size.width - 2 * nameLabel.frame.origin.x, 0)];
contentLabel.font = [UIFont systemFontOfSize:14];
contentLabel.numberOfLines = 0;
[self.contentView addSubview:contentLabel];
self.contentLabel = contentLabel;
UILabel *timeLabel = [[UILabel alloc] init];
timeLabel.font = [UIFont systemFontOfSize:12];
timeLabel.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width - nameLabel.frame.origin.x, [timeLabel.font singleLineHeight]);
timeLabel.textAlignment = NSTextAlignmentRight;
timeLabel.textColor = [UIColor grayColor];
[self.contentView addSubview:timeLabel];
self.timeLabel = timeLabel;
}
- (void)setModel:(PlaySuccessModel *)model
{
_model = model;
_nameLabel.text = model.userName;
_contentLabel.text = model.content;
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy年MM月dd日 HH:mm";
_timeLabel.text = [formatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:model.time]];
// 位置移动
CGFloat height = [_contentLabel.text boundingRectWithSize:CGSizeMake(_contentLabel.frame.size.width, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:_contentLabel.font} context:nil].size.height;
_contentLabel.frame = (CGRect){_contentLabel.frame.origin,{_contentLabel.frame.size.width,height}};
CGRect tempRect = _timeLabel.frame;
tempRect.origin.y = _contentLabel.frame.origin.y + _contentLabel.frame.size.height + 10;
_timeLabel.frame = tempRect;
}
至于我们常用的其他数据源方法,我也都进行了封装。
- (NSInteger)listViewHasNumberOfRows:(KWListView *)listView
- (CGFloat)listView:(KWListView *)listView heightForRow:(NSInteger)row
关于上一个方法,我们在上面的 listView.requestAction 这段 block 中,已经将获取数据的方法传给了 listView,所以 listView 会默认显示它请求到的数据量,或者说我们规定的一页请求量,如果你有特殊要求,那么你还可以按照 UITableView 的处理方式来处理。
对于下面关于高度的方法,我们有自动高度计算的机制,所以可以不用实现。那么高度自动计算到底怎么用呢?我们下面来说说
高度自动计算实现依赖于我在 listView 中封装的一个缓存池,就是根据 cell 工厂方法中 cell 的布局及数据对 cell 的高度进行计算。
在 listView 中有一个属性 bottomMargin,这个属性是用来说明 cell 中最下面的视图与 cell 底部的距离的。如果不设置这个属性,那么 listView 将按照 cell 最上面视图与 cell 顶部的距离进行计算。
还有一个很重要的值,KWUncountTag,它用于忽略 cell 中的视图。 在 cell 中,更确切地说是在 cell.contentView 中的所有视图,除了隐藏的视图或视图 tag 为 KWUncountTag 的视图不会被计算高度,其余都会。
**关于 KWListView 的内容还有很多,由于精力有限,不能一一说明,更多内容请下载代码参考 Demo,或者查看源码进行了解。
如果在使用过程中发现任何 Bug,请联系我修改,谢谢~