- 设置tableView高度并自适应高度设置
模拟器监测FPS最低27fps,真机监测最低58fps
tableView.estimatedRowHeight = 100;
tableView.rowHeight = UITableViewAutomaticDimension;
设置cell自适应高度的约束
UILabel *textLab = [[UILabel alloc]init];
textLab.numberOfLines = 0;
[self addSubview:textLab];
self.textLab = textLab;
[textLab mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self).offset(10);
make.left.mas_equalTo(self).offset(5);
make.bottom.mas_equalTo(self).offset(-5);
make.right.mas_equalTo(self.mas_right).offset(-5);
}];
如果我们用了自动计算高度的方法,又调用了tableView的reloadData方法(例如我们的数据有分页的时候,加载完下一页的数据后会去刷新tableView)。可能会出现问题,点击状态栏就有几率不能精确滚动到顶部了,那就cell高度做缓存,直接上代码。
@property (nonatomic, strong) NSMutableDictionary *heightAtIndexPath;//缓存高度所用字典
#pragma mark - UITableViewDelegate
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber *height = [self.heightAtIndexPath objectForKey:indexPath];
if(height)
{
return height.floatValue;
}
else
{
return 100;
}
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber *height = @(cell.frame.size.height);
[self.heightAtIndexPath setObject:height forKey:indexPath];
}
用一个字典做容器,在cell将要显示的时候在字典中保存这行cell的高度。然后在调用estimatedHeightForRowAtIndexPath方法时,先去字典查看有没有缓存高度,有就返回,没有就返回一个大概高度。
缓存高度之后,在demo里面多试几次,发现点击状态栏已经可以精确滚动回顶部了。
- 2.计算变化的富文本高度进行赋值加上其它控件的高度,这里就不写啦。
- 3.基于UITableView+FDTemplateLayoutCell.h 大神写的
附上链接
//1.导入头文件
#import
// cell标识符
static NSString *cellID = @"cell";
//2.打印台日志输出
tableView.fd_debugLogEnabled = YES;
//3.tableview数据源 configuration后面id类型 更换你自定义的TableViewCell
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [_tableView fd_heightForCellWithIdentifier:cellID cacheByKey:indexPath configuration:^(TableViewCell *cell) {
cell.textLab.text = self->arr[indexPath.row];
}];
}
//#import "TableViewCell.h"
//4.自定义的TableViewCell 一定添加到self.contentView上面否则无效果
-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
[self setUpView];
}
return self;
}
-(void)setUpView
{
UILabel *textLab = [[UILabel alloc]init];
textLab.numberOfLines = 0;
textLab.font = [UIFont systemFontOfSize:14];
[self.contentView addSubview:textLab];
self.textLab=textLab;
[textLab mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.contentView).offset(10);
make.left.mas_equalTo(self.contentView).offset(5);
make.bottom.mas_equalTo(self.contentView).offset(-5);
make.right.mas_equalTo(self.contentView.mas_right).offset(-5);
}];
}
###### 写完上面的代码后,你就已经使用到了:
> 和每个 UITableViewCell ReuseID 一一对应的 template layout cell
这个 cell 只为了参加高度计算,不会真的显示到屏幕上;它通过 UITableView 的 -dequeueCellForReuseIdentifier: 方法 lazy 创建并保存,所以要求这个 ReuseID 必须已经被注册到了 UITableView 中,也就是说,要么是 Storyboard 中的原型 cell,要么就是使用了 UITableView 的 -registerClass:forCellReuseIdentifier: 或 -registerNib:forCellReuseIdentifier:其中之一的注册方法。
> 根据 autolayout 约束自动计算高度
使用了系统在 iOS6 就提供的 API:-systemLayoutSizeFittingSize:
> 根据 index path 的一套高度缓存机制
计算出的高度会自动进行缓存,所以滑动时每个 cell 真正的高度计算只会发生一次,后面的高度询问都会命中缓存,减少了非常可观的多余计算。
> 自动的缓存失效机制
无须担心你数据源的变化引起的缓存失效,当调用如-reloadData,-deleteRowsAtIndexPaths:withRowAnimation:等任何一个触发 UITableView 刷新机制的方法时,已有的高度缓存将以最小的代价执行失效。如删除一个 indexPath 为 [0:5] 的 cell 时,[0:0] ~ [0:4] 的高度缓存不受影响,而 [0:5] 后面所有的缓存值都向前移动一个位置。自动缓存失效机制对 UITableView 的 9 个公有 API 都进行了分别的处理,以保证没有一次多余的高度计算。
> 预缓存机制
预缓存机制将在 UITableView 没有滑动的空闲时刻执行,计算和缓存那些还没有显示到屏幕中的 cell,整个缓存过程完全没有感知,这使得完整列表的高度计算既没有发生在加载时,又没有发生在滑动时,同时保证了加载速度和滑动流畅性,下文会着重讲下这块的实现原理。
利用RunLoop空闲时间执行预缓存任务
FDTemplateLayoutCell 的高度预缓存是一个优化功能,它要求页面处于空闲状态时才执行计算,当用户正在滑动列表时显然不应该执行计算任务影响滑动体验。
一般来说,这个功能要耦合 UITableView 的滑动状态才行,但这种实现十分不优雅且可能破坏外部的 delegate 结构,但好在我们还有RunLoop这个工具,了解它的运行机制后,可以用很简单的代码实现上面的功能。
最后附上文章GitHub Demo链接 传送GitHub Demo
参考链接 UITableView+FDTemplateLayoutCell.h 博客