估算机制
在iOS7及iOS7以下的系统中,当tableView要展示时,会调用代理的数据源方法- tableView: heightForRowAtIndexPath:
(tableView获取高度默认访问rowHeight属性,实现了这个方法后,rowHeight属性就会失效,所以对于等高的cell来说, rowHeight能够减少许多不必要的计算和方法调用的开销)获得所有row
的高度以确定scrollView
的contentSize
.
- tableView: heightForRowAtIndexPath:
适用于不等高cell(没实现该数据源方法就使用rowHeight
属性值)
rowHeight
适用于等高cell (rowHeight在iOS7中默认为44.f,iOS8为-1.f,即UITableViewAutomaticDimension)
以上两种用于计算cell的高度的方法,都是在tableView将要显示的时候,一次性计算出所有cell.这个时候,如果所展示的cell很多,并且每个cell的高度也很高,那么耗费在计算高度的初始时间将会多很多,所以苹果在iOS7的时候推出了estimatedRowHeight
这个属性(与之类似的还有estimatedSectionHeaderHeight
,estimatedSectionFooterHeight
).
顾名思义,这个属性的意义在于估算.有了这个估算高度,就能确定contenSize
啦,但是这个contenSize
是个估算值,是通过estimatedRowHeight
xcell的个数
得到初始的contenSize
中的高度,并不是最终的contenSize
.实现了这个属性后,tableView就不会一次性计算所有的cell的高度了,只会计算当前屏幕能够显示的cell个数加上几个.滑动时,tableView不停地得到新的cell,调用数据源方法得到高度,更新自己的contenSize
,在滑到最后的时候,会得到正确的contenSize
,在这过程中,旁边的滚动条会不可避免地"抖动",因为contenSize
在不停地更新.因此,在设置estimatedRowHeight
时要给出尽量接近cell高度平均值的数值,让"抖动"更小.
// 从打印能看出,contentSize会按照一定的算法进行更新
self.tableView.estimatedRowHeight = 100;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(@"%@",NSStringFromCGSize(tableView.contentSize));
return 300;
}
此外,iOS7中每个cell的高度会被系统自动缓存起来,不会再重复计算了.
在iOS7上,设置一个cell的尺寸有两种方式:
- AutoLayout
- Manual-sizing code (即人工手算)
第二种方式比较常见,通常我们通过在tableView: heightForRowAtIndexPath:
方法中用cell对应的模型中的数据属性直接计算获得返回高度,在利用数据计算高度完成后 还可以将子控件的frame属性写入模型中,方便在模型中赋值布局子控件
其实第一种方式也很方便,最需要注意的是要设置好完整的约束.系统提供给了我们一个API叫- systemLayoutSizeFittingSize:
,可以通过约束计算到cell的高度.
// 设置一个属性,或者是静态的全局变量的cell.因为这个cell是为了计算高度而生,不必每次调用方法都创建.
@property (nonatomic, strong) XXXTableViewCell *prototypeCell;
// 初始化时
self.prototypeCell = (XXXTableViewCell *)[self.tableView dequeueReusableCellWithIdentifier:@"XXXTableViewCell"];
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// 你或许会想到调用- tableView:cellForRowAtIndexPath方法拿到当前indexPath的cell,然后通过systemLayoutSizeFittingSize方法来计算高度.可是显示tableView之前只有拿到高度才能创建cell实例.如果在这里面调用该方法,会死循环.
XXXTableViewCell *cell = self.prototypeCell;
//通过设置cell中的Model属性来计算高度并保存在模型的属性中
cell.model = [self.modelArray objectAtIndex:indexPath.row];
CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
// 为什么要加上一个奇怪的数呢? 因为这里计算的只是cell的contentView的高度,分割线还占1个像素的高度.
return 1.0 / [UIScreen mainScreen].scale + size.height;
}
另外需要注意的是,在iOS7下(iOS8没有这个问题),如果布局中有UILabel
,并且行数大于0时,需要指定preferredMaxLayoutWidth
,这样Label才能知道自己什么时候该换行,然后- systemLayoutSizeFittingSize
才能得到正确的高度.
另外,self-sizing cell
的另外一个重要功能就是实现了自动算高,不过需要满足2个条件:
- 使用Autolayout进行布局
- 设置行高方式为自动计算 self.tableView.rowHeight = UITableViewAutomaticDimension
设置estimatedRowHeight
的值self.tableView.estimatedRowHeight = 44
然后就OK了.
1.例如:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
ResultBalloonCell *cell = [ResultBalloonCell cellWithTableView:tableView];
self.tableResult.rowHeight = UITableViewAutomaticDimension;
self.tableResult.estimatedRowHeight = 100;
cell.message = [self.translatedDataArray objectAtIndex:indexPath.row];
CGFloat height3 = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
[cell.contentView setNeedsLayout];
[cell.contentView layoutIfNeeded];
return (height3);
}