场景描述:
最近项目做到一个评论列表的页面,如下图 :
这样的一个列表,其实没有多复杂,这边我最开始做法是使用 UITableViewCell 内部自动布局,要使用自动布局,我们需要去设置 tableView 的估算高度 estimatedRowHeight ,并且设置 rowHeight 为自动。
self.tableView.estimatedRowHeight = 150.0f;
self.tableView.rowHeight = UITableViewAutomaticDimension;
而且我们不需要再实现 tableView 代理方法来返回 Cell 高度了
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { }
只需要保证 Cell 内部 UI 的布局最后能够闭合的撑开 Cell 即可。
最后用自动布局完成了页面,并且在所有的系统下也都经过测试,没有出现任何 UI 问题,并且也没有约束报错的情况。
但是,在这之后,我发现 UITableViewCell 如果使用自动布局,它有优点,但也有一定的缺点,(这个缺点后面会说明) 所以,为了处理这个缺点所带来的问题,这边就得出实现 heightForRowAtIndexPath 方法来返回 Cell 高度。 这边使用的是 FDTemplateLayoutCell 来计算出 Cell 高度。 UITableView-FDTemplateLayoutCell
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
WEAKSELF
return [tableView fd_heightForCellWithIdentifier:[BlogsCommentsTableViewCell reuseIdentifier] cacheByIndexPath:indexPath configuration:^(id cell) {
[cell setupWithModel:weakSelf.dataSource[indexPath.row]];
}];
}
Cell 内部的约束没有做任何改变,只是增加实现了 heightForRowAtIndexPath 方法,后来经过测试,除了 iOS 10.3 系统下,其他的系统下均正常显示,并且之前发现的缺点也一并的解决了。 而在 iOS 10.3 系统下,是报出了约束冲突的错误,最开始以为是自己的布局方式,在iOS 10.3 的系统下会有些许的差异化,或者说 在这个系统下,某些 UI 不能按照现在的布局,因为这个猜想,自己是换了好几种布局方式,在保证其他系统不受影响的情况下,来修改 Cell 内部布局的方式,在确保自己的自动布局约束一定没有问题的情况下,仍然会出现约束冲突的错误。。。这边截取部分报错的关键信息如下:
等等多条约束报错信息,意思就是告诉你,这条约束可能不需要。
在网上也搜索到部分约束报冲突的解决方式就是冲突的那条约束增加 .priorityHigh(),列如:
[self.emojiLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.headImageView.mas_bottom).offset(FitScreenSize(10)).priorityHigh();
make.leading.equalTo(self.headImageView.mas_trailing).offset(FitScreenSize(20));
make.trailing.equalTo(@(-RightMargin_Constant));
make.bottom.equalTo(self.fullTextButton.mas_top);
}];
前面的报错信息告诉我们 self.emojiLabel 的 top 约束有冲突,所以按照网上的增加 .priorityHigh()
make.top.equalTo(self.headImageView.mas_bottom).offset(FitScreenSize(10)).priorityHigh();
只能说这种方式确实可以解决部分约束的冲突,在我这边确实为我处理了控制台约束报错的 99%。但是本文中说的 iOS10.3 系统下的约束,却不可用这种方式解决,虽然大部分都处理了,可最后你会发现剩下最后一条约束报错让你很懵逼。。。因为你无从下手修改。。 如下:
为什么说无从下手修改? 首先解释下这些报错信息,它是说你的 UITableViewCell 有2条 width 约束限制着,witdh = 0 的这条,我很好理解,毕竟视图刚创建出来的时候,还没真正布局的时候并没有宽高的。 但是 width = 375 ,真的很让人很费解,自己刚开始也看不懂为什么会多这条出来,而且自己确实没有强加过这条约束, 后面的提示 Will attempt to recover by breaking constraint . 确实是告诉你给 UITableViewCell 没必要设置 width = 375 的约束,(375 是根据 手机/模拟器 的屏幕宽来决定的,会变化要注意)。。。
又几经周折过后,最终确定问题的根本原因!!并不是自己写的约束有问题,而是使用了 UITableView-FDTemplateLayoutCell 来计算 Cell 高度时,这个库内部使用 systemLayoutSizeFittingSize 来获取布局控件的高度,这个方法在 iOS 10.3 系统下会自动约束上 width 和 height ,而我们自己的 Cell 内部布局方式是撑开 Cell 自身的,所以 systemLayoutSizeFittingSize 又给加了 width 约束,一边要自动布局,一边又设置了 width 和 height,所以最终约束冲突,并且也能解释为什么会多一条 width = 375 的约束了。。。根本问题找到了,就好解决了。
PS: 点击这里查看在GitHub上,大家讨论FDTemplateLayoutCell在iOS10.3系统下错误情况。
言简意赅,我的解决方式就是 :
1、在设置 TableView 时,判断是否是 iOS 10 的系统,如果是的话,依旧使用自动布局,因为 Cell 内部约束本身就是没问题的。
if (@available(iOS 10.0, *)) { // 10 系统下依旧使用自动布局
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 150.0f;
} else { // 其他系统下
// 防止 iOS11 Section展开表格跳动Bug
if (IOS11_OR_LATER) {
self.tableView.estimatedRowHeight = 0;
self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;
}
}
2、在 heightForRowAtIndexPath 做下简单处理 , 增加了判断 10 系统下不计算的情况,如下 :
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (@available(iOS 10.0, *)) { // 10系统下不计算
return UITableViewAutomaticDimension;
}
WEAKSELF
return [tableView fd_heightForCellWithIdentifier:[BlogsCommentsTableViewCell reuseIdentifier] cacheByIndexPath:indexPath configuration:^(id cell) {
[cell setupWithModel:weakSelf.dataSource[indexPath.row]];
}];
}
到这里,iOS10.3 系统下使用 FDTemplateLayoutCell 导致约束冲突已解决。