在我正在创建的这个iOS 8应用程序中,我有一个tableview,我需要它们能够自动调整大小。我使用自动布局实现了它,它工作正常。几乎这是它现在的样子
一个单元格内有3个标签。包含lorem ipsum文本的主标签。包含数字字符串的副标题(这是两个单独的标签。可能会混淆,因为它们具有相同的颜色。)然后是第三个带有黑色小文本的标签
第一个标签正确地调整了大小,没有问题,第二个标签也相应地上下移动。但问题在于第三个小标签。正如您所看到的,它不会调整自身大小以适应所有文本
现在发生了一件奇怪的事情。我把它变成风景,就是这样
因为有空间,标签显示的是它应该显示的整个文本。好的然后我把它转回到肖像画
现在,小标签已经调整了自身大小,以适应其所有文本,但它溢出了单元格边界。我试着把手机放大,但没用。由于这是自调整大小的细胞,我甚至认为这不是正确的方法
我的自动布局约束也没有收到任何错误甚至警告
我已经在viewDidLoad()方法中设置了这两行代码
tableView.estimatedRowHeight=100
tableView.rowHeight=UITableViewAutomaticDimension
有谁能告诉我我可能做错了什么
因为仅仅通过查看图片很难回答,而且除了上面的代码片段之外,我没有更多的代码可以发布,所以我在这里上传了一个可运行的Xcode项目来演示这个问题。(有两个自定义单元格。基本上是相同的单元格,只是第二个单元格的高度增加了。)
我一直在摆弄自动布局约束,但我似乎无法让它工作。任何帮助都将不胜感激
多谢各位
更新:
在本教程的帮助下,我找到了一些有用的指针。根据它,每个子视图都应该有约束来固定其所有边,并且应该有从上到下的约束,这有助于自动布局来计算单元的高度。在我最初的帖子中,我在每个标签之间都有垂直间距,所以我认为这就是auto layout无法计算正确高度的原因
所以我做了一些改变
- 我将标签之间的垂直空间减小为0,并设置顶部和中部标签以及中部和底部标签之间的垂直空间约束
- 我向顶部标签添加了前导、顶部和尾部约束
- 前导和尾随到中间标签
- 前导、底部、尾随至底部标签
这是另一个奇怪的部分。当我第一次运行它时,底部标签裁剪问题仍然存在
但是如果我将设备旋转到横向并将其返回到纵向,则所有单元格的大小都会正确调整,以适合两个标签
但还是不明白为什么一开始不会发生这种情况。更新的Xcode项目在这里
这里的问题是多行标签“preferredMaxLayoutWidth属性。这个属性告诉标签何时应该换行。为了使每个标签的intrinsicContentSize具有正确的高度,必须正确设置它,这最终是Auto Layout将用于确定单元格高度的内容
Xcode 6 Interface Builder引入了一个新选项,将此属性设置为自动。不幸的是,在从nib或故事板加载单元格时,存在一些严重的错误(从Xcode 6.2/iOS 8.2开始),这些错误没有正确/自动设置
为了解决这个错误,我们需要将preferredMaxLayoutWidth设置为完全等于标签在表视图中显示后的最终宽度。实际上,我们希望在从表视图返回单元格之前执行以下操作:cellforrowatinexpath::
cell.namelab.preferredMaxLayoutWidth=CGRectGetWidth(cell.namelab.frame)
cell.idLabel.preferredMaxLayoutWidth=CGRectGetWidth(cell.idLabel.frame)
cell.actionsLabel.preferredMaxLayoutWidth=CGRectGetWidth(cell.actionsLabel.frame)
仅添加此代码不起作用的原因是,当这三行代码在tableView:CellForRowatineXpath:中执行时,我们使用每个标签的宽度来设置preferredMaxLayoutWidth——但是,如果此时检查标签的宽度,标签宽度与显示单元及其子视图后的标签宽度完全不同
在这一点上,我们如何获得准确的标签宽度,以便它们反映其最终宽度?下面是将所有这些结合在一起的代码:
//tableView:cellForRowAtIndexPath:的内部,在将单元格退出队列后
cell.bounds=CGRect(x:0,y:0,宽度:CGRectGetWidth(tableView.bounds),高度:99999)
cell.contentView.bounds=cell.bounds
cell.layoutIfNeeded()单元格
cell.namelab.preferredMaxLayoutWidth=CGRectGetWidth(cell.namelab.frame)
cell.idLabel.preferredMaxLayoutWidth=CGRectGetWidth(cell.idLabel.frame)
cell.actionsLabel.preferredMaxLayoutWidth=CGRectGetWidth(cell.actionsLabel.frame)
好的,那么我们在这里做什么?您会注意到添加了3行新代码。首先,我们需要设置这个表视图单元格的宽度,使其与表视图的实际宽度相匹配(这假设表视图已经布置好并具有最终宽度,应该是这样)。我们实际上只是在早期修正单元格宽度,因为表视图最终会这样做
您还会注意到,我们使用的高度是99999。那是怎么回事?对于这里详细讨论的问题,这是一个简单的解决方法,如果您的约束需要比单元格contentView的当前高度更多的垂直空间,那么您会得到一个约束异常,它实际上并不表示任何实际问题。此时,单元或其任何子视图的高度实际上并不重要,因为我们只关心每个标签的最终宽度
接下来,通过将contentView的边界设置为与单元格的边界相等,确保单元格的contentView的大小与刚才分配给单元格本身的大小相同。这是必要的,因为您创建的所有自动布局约束都是相对于contentView的,因此contentView的大小必须正确,才能正确求解。仅手动设置单元格大小不会自动调整contentView的大小以匹配
最后,我们在单元上强制进行布局传递,这将使自动布局引擎解决您的约束并更新所有子视图的框架。自细胞&;contentView现在具有与运行时在表视图中相同的宽度,标签宽度也将是正确的,这意味着为每个标签设置的preferredMaxLayoutWidth将是准确的,并将导致标签在正确的时间换行,这当然意味着在表格视图中使用单元格时,标签的高度将正确设置
这肯定是UIKit中的一个苹果bug,我们现在必须解决它(所以请向苹果提交bug报告,以便他们确定修复的优先级!)
最后一点注意:如果您的表视图单元格的contentView宽度没有扩展表视图的整个宽度,例如当右侧显示一个节索引时,此解决方法将遇到问题。在这种情况下,您需要确保在设置单元格宽度时手动考虑到这一点——您可能需要硬编码这些值,例如:
让cellWidth=CGRectGetWidth(tableView.bounds)-kTableViewSectionIndexWidth
cell.bounds=CGRect(x:0,y:0,宽度:cellWidth,高度:99999)