那么现在问题来了,如何才能让cell正确计算自己的高度,这就要使用到Autolayout了,无论是通过xib文件创建的cell还是代码创建的cell,若想让cell自动正确的计算出自身的高度,必须添加足够压力的约束。所谓足够压力,是指UITableViewCell的contentView的上、下、左、右必须被内部控件的约束所撑满,需要注意,cell上的视图必须添加在contentView上,否则计算会出现问题。
例如下图所示,左侧的图标进行了与父视图的左侧距离约束,标题Label进行了与父视图的上侧距离约束和右侧距离约束,内容Label进行了与标题Label的上侧约束和与父视图的下册约束,并且对宽度进行了约束。此时,UITableViewCell的contentView四周都被子视图进行了约束,可以想象,内容Label的文本长度是不定的,当文本长度是的内容Label进行换行,内容Label的高度改变的时候,contentView下册会受到内容Label施加的压力,这时cell也会根据约束自动扩充自己的高度。
示例代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"表视图";
_tableView = [[UITableView alloc]initWithFrame:self.view.frame style:UITableViewStylePlain];
[_tableView registerNib:[UINib nibWithNibName:@"TableViewCell" bundle:nil] forCellReuseIdentifier:@"cellid"];
_tableView.delegate = self;
_tableView.dataSource = self;
//设置一个模糊的行高用于配置TableView右侧滚动条
_tableView.estimatedRowHeight = 60;
[self.view addSubview:_tableView];
titleArray = @[@"标题1",@"标题2",@"标题3",@"标题4",@"标题5",@"标题6",@"标题7",@"标题8",@"标题9",@"标题10"];
detailArray = @[@"内容内容内容内容内容内容内容内容内容",
@"内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容",
@"内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容",
@"内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容",
@"内容内容容内容内内容内容内容内容内容内容内容",
@"容内容内内容内容",
@"内容内容内容内容容内容内容内容",
@"内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容",
@"内容内容内容内容内容内容容内容内内容内容内容",
@"内容内容内容内容内容内容内容内容内容"];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 10;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
TableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"cellid" forIndexPath:indexPath];
cell.title.text = titleArray[indexPath.row];
cell.detail.text = detailArray[indexPath.row];
return cell;
}
通过上面示例可以看到,十分简单的代码完美的解决了图文混排cell高度的自适应。Autolayout真的是一种十分强大的技术😄。
关于细节方面,还有一个问题需要注意,预估的行高会影响到TableView右侧滚动条的展现,如果每个cell行高跳跃跨度十分大,滚动条宽度的配置会失准,随着用户滑动表视图,右侧滚动条可能会出现长短跳跃的情况,如果开发者需要精准这个滚动条的配置,可以在如下代理方法中返回具体cell的估计行高。
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{
//这里根据不同分区 或者不同行 设置估计的行高
return 44;
}
关于estimatedHeightForRowAtIndexPath方法其实还有一种应用场景,前面介绍的优化方式都是以Autolyout为前提,对于没有使用自动布局,cell的高度需要手动计算的场景中,如果实现了这个方法,并且实现了heightForRowAtIndexPath方法,heightForRowAtIndexPath方法会以懒加载的方式执行,只有在cell将要展现在屏幕上时heightForRowAtIndexPath方法才会被执行,这也可以有效减小由于高度计算带来的性能负担。
三、关于高度不定的UITableView分区头尾视图
一般情况下,TableView的分区头尾视图高度都是固定的,因此一般不需要考虑计算分区头尾视图高度产生的性能问题,类比如cell的布局原理,其实分区头尾视图也可以通过Autolayout实现自适应高度,示例代码如下:
//返回一个估计的分区头视图高度
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForHeaderInSection:(NSInteger)section{
return 10;
}
//使用自动布局给头视图添加足够的布局压力
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
UIView * view = [[UIView alloc]init];
UILabel * label = [[UILabel alloc]init];
label.numberOfLines = 0;
if (section==0) {
label.text = @"头视图头视图头视图";
}else{
label.text = @"头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图头视图";
}
[view addSubview:label];
[label mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(@10);
make.right.equalTo(@-10);
make.top.equalTo(@10);
make.bottom.equalTo(@-10);
}];
return view;
}
效果如下图:
分区为视图的设置方式与头视图一样。
UITableView类中还有一个十分有趣的常量:
UIKIT_EXTERN const CGFloat UITableViewAutomaticDimension;
UITableViewAutomaticDimension是一个CGFloat类型的常量,其需要和用来处理返回头尾视图标题的方法结合使用,用它来作为TableView分区头尾视图的高度返回,系统会自动根据标题是否存在来进行自适应,举个例子,如果返回的标题为nil,则头视图会被自动隐藏,示例代码如下:
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
//视图为nil则会自动返回0
return UITableViewAutomaticDimension;
}
-(NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
if (section==0) {
return nil;
}else{
return @"头视图头视图头视图头视图头视图头视图头视图头视";
}
}
小提示:UITableViewCell在创建出来时,其宽度并不一定和UITableView宽度一致,如果开发者需要通过获取cell的宽度来处理逻辑,要在cell的layoutSubViews里面进行,此时cell的宽度才正确。