cell高度自适应
cell自适应的怨念
iOS7中估算cell 高度的方法:estimatedRowHeight
1 | self.tableView.estimatedRowHeight = 88; |
估算高度出现的问题:
- 设置估算高度后,contentSize.height 根据“cell估算值 x cell个数”计算,这就导致滚动条的大小处于不稳定的状态,contentSize 会随着滚动从估算高度慢慢替换成真实高度,肉眼可见滚动条突然变化甚至“跳跃”。
- 若是有设计不好的下拉刷新或上拉加载控件,或是 KVO 了
contentSize
或contentOffset
属性,有可能使表格滑动时跳动。 - 估算高度设计初衷是好的,让加载速度更快,但损害了滑动的流畅性,滑动时实时计算高度会带来明显的卡顿。
iOS8中self-sizing cell——cell自己负责对自身高度的计算
有两种方式:
- fram layout下:重写
sizeThatFits
方法 - auto layout下: add constraints to cell.contenView
这个特性只支持iOS8以上的系统,用到的API:
1 | self.tableView.estimatedRowHeight = 213; |
所以,需要支持iOS7的项目,还是需要自己实现高度自适应。
第三方实现——FDTemplateLayoutCell
所有的附加文件都是UITableView的category,共有四个类:
- UITableView+FDTemplateLayoutCell
- UITableView+FDKeyedHeightCache
- UITableView+FDIndexPathHeightCache
- UITableView+FDTemplateLayoutCellDebug
UITableView+FDTemplateLayoutCell
这个category把tableView
中用到的不同样式的cell
封装成一个一个的模板cell
,然后计算该模板cell
的高度。
高度计算过程
使用auto layout计算高度(cell使用了auto layout):通过
systemLayoutSizeFittingSize:
计算cell高度1
fittingHeight = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
警告:一旦在使用auto Layout的情况下,步骤1返回的高度为0,则报警告
使用frame layout计算高度(cell可能没有使用auto layout):如果步骤1返回高度为0,则调用
sizeThatFits:
方法计算cell高度如果返回高度仍为0,就自己给一个合适的高度或默认的高度44,然后根据需要把高度给缓存类或者UITableView的heightForRow
注意:
计算得到的高度不包括 separator view
的高度,所以在返回高度之前,会根据separator style
来增加1px
1 | if (self.separatorStyle != UITableViewCellSeparatorStyleNone) { |
UITableView+FDKeyedHeightCache
根据key,比如reuseID,给cell做高度缓存。
比如,有三种不同类型的cell,每种cell的高度的高度是一样的,但三种类型各不相同,这时候可以用这个方法缓存高度。
UITableView+FDIndexPathHeightCache
根据indexPath进行高度缓存。
比如在tableView reloadData的时候,如果根据indexPath做了缓存,就不需要再次计算
UITableView+FDTemplateLayoutCellDebug
主要是在debug的时候添加一些log信息,方便debug
cell自适应高度的自定义实现
auto layout下:
给cell的subViews添加合适的约束
给tableView设置估算高度
给
cell.contentView
添加约束。因为cell.contentView
必须要有super view
(就是tableView)才能知道自身的宽度,然后才能保证layout出合适的高度。给contentView添加约束:
1
2
3
4
5
6
7CGFloat contentViewWidth = CGRectGetWidth(self.tableView.bounds);
NSLayoutConstraint *widthFenceConstraint = [NSLayoutConstraint constraintWithItem:cell.contentView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:contentViewWidth];
[cell.contentView addConstraint:widthFenceConstraint];
// Auto layout engine does its math
fittingHeight = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
[cell.contentView removeConstraint:widthFenceConstraint];在heightForRow的时候,创建cell——>这个cell只用来计算需要的cell高度,并不绘制在tableView上,因为自适应条件下要获取cell高度,需要根据cell内容来计算cell高度,所以首先要有一个cell。
注意:cell.contentView
的约束是在获取height的方法里添加的,里面根据accessoryType
的不同设置了不同的contentView
的宽度(accessoryType
不同,accessory的宽度不包含在contentView中)。