这里的问题是多行标签的preferredMaxLayoutWidth
属性.这个属性告诉标签何时应该换行.必须正确设置,才能使每个标签的intrinsicContentSize
具有正确的高度,这最终是Auto Layout用来确定单元格高度的方法.
Xcode 6 Interface Builder引入了一个新选项,将此属性设置为Automatic.不幸的是,在从nib或故事板加载单元格时,存在一些严重的错误(从Xcode 6.2/iOS 8.2开始),这些错误没有正确/自动设置.
为了解决这个问题,我们需要将preferredMaxLayoutWidth
设置为完全等于标签在表视图中显示后的最终宽度.实际上,在从tableView:cellForRowAtIndexPath:
返回单元格之前,我们希望执行以下操作:
cell.nameLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.nameLabel.frame)
cell.idLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.idLabel.frame)
cell.actionsLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.actionsLabel.frame)
仅添加此代码不起作用的原因是,当这3行代码在tableView:cellForRowAtIndexPath:
中执行时,我们使用每个标签的宽度来设置preferredMaxLayoutWidth
——但是,如果此时判断标签的宽度,标签宽度与显示单元及其子视图后的标签宽度完全不同.
在这一点上,我们如何使标签宽度准确,从而反映其最终宽度?以下是将所有这些结合在一起的代码:
// Inside of tableView:cellForRowAtIndexPath:, after dequeueing the cell
cell.bounds = CGRect(x: 0, y: 0, width: CGRectGetWidth(tableView.bounds), height: 99999)
cell.contentView.bounds = cell.bounds
cell.layoutIfNeeded()
cell.nameLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.nameLabel.frame)
cell.idLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.idLabel.frame)
cell.actionsLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.actionsLabel.frame)
好的,那我们在这里做什么?您会注意到添加了3行新代码.首先,我们需要设置这个表视图单元格的宽度,使其与表视图的实际宽度相匹配(这假设表视图已经布置好,并具有最终宽度,应该是这样).我们实际上只是在早期修正单元格宽度,因为表格视图最终会这样做.
你也会注意到我们用99999
表示高度.那是怎么回事?对于细节here中讨论的问题,这是一个简单的解决方法,如果您的约束需要比单元格contentView的当前高度更多的垂直空间,那么您会得到一个约束异常,它实际上并不表示任何真正的问题.此时,单元或其任何子视图的高度实际上并不重要,因为我们只关心每个标签的最终宽度.
接下来,我们将contentView的边界设置为与单元格的边界相等,以确保单元格的contentView的大小与刚才分配给单元格本身的大小相同.这是必要的,因为您创建的所有自动布局约束都是相对于contentView的,因此contentView的大小必须正确,才能正确求解.只需手动设置单元格大小,contentView就会自动调整大小以匹配.
最后,我们在单元上强制进行布局传递,自动布局引擎将解决您的约束并更新所有子视图的框架.自从细胞&;contentView现在具有与运行时在表视图中相同的宽度,标签宽度也将是正确的,这意味着 for each 标签设置的preferredMaxLayoutWidth
将是准确的,并将导致标签在正确的时间换行,这当然意味着当单元格在表视图中使用时,标签的高度将正确设置!
这绝对是UIKit中的一个苹果漏洞,我们现在必须解决它(所以请对苹果执行file bug reports次操作,以便他们优先考虑修复!).
最后一点注意:如果表视图单元格的contentView
宽度没有扩展表视图的全宽度,例如当右侧显示一个节索引时,这种解决方法将遇到问题.在这种情况下,您需要确保在设置单元格宽度时手动考虑到这一点——您可能需要对这些值进行硬编码,例如:
let cellWidth = CGRectGetWidth(tableView.bounds) - kTableViewSectionIndexWidth
cell.bounds = CGRect(x: 0, y: 0, width: cellWidth, height: 99999)