在我创建的这个iOS 8应用程序中,我有一个tableview,我需要它们自动调整大小.我用Auto Layout实现了它,而且很有效.几乎.下面是它现在的样子.

在此处输入图像描述

一个单元格内有3个标签.带有lorem ipsum文本的主标签.包含一串数字的副标题(这是两个独立的标签.可能会混淆,因为它们的 colored颜色 相同.)然后是第三个带有黑色小文本的标签.

第一个标签正确地调整了大小,没有问题,第二个标签也相应地上下移动.但问题在于第三个小标签.正如你所见,它并没有调整自身大小以适应所有文本.

现在发生了一件奇怪的事情.我把它变成风景,就是这样.

在此处输入图像描述

因为有空间,标签显示的是它应该显示的全部文本.好的然后我把它转回到肖像.

在此处输入图像描述

现在,小标签已经调整了大小,以适应所有文本,但它溢出了单元格边界.我试着把电池变大,但没有成功.由于这是一种自动调整细胞大小的方法,我甚至不认为这是正确的方法.

我的自动布局约束也没有收到任何错误甚至警告.

在此处输入图像描述

我在viewDidLoad()方法中设置了这两行代码.

tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension

谁能告诉我我可能做错了什么吗?

因为仅仅通过查看图片很难回答,而且除了上面的代码片段之外,我没有更多的代码可以发布,所以我上传了一个可运行的Xcode项目来演示问题here.(有两个自定义单元格.基本上是同一个单元格,只是第二个单元格的高度增加了.)

我一直在摆弄自动布局约束,但我似乎无法让它工作.任何帮助都将不胜感激.

非常感谢.


UPDATE:

在这tutorial的帮助下,我找到了一些有用的指针.根据它,每个子视图都应该有约束来固定其所有边,并且应该有从上到下的约束,这有助于自动布局来计算单元的高度.在我最初的帖子中,我在每个标签之间有垂直间距,所以我认为这就是auto layout无法计算正确高度的原因.

所以我做了一些改变.

  • 我将标签之间的垂直空间减少为0,并在顶部和中部标签以及中部和底部标签之间设置垂直空间约束.
  • 我向顶部标签添加了前导、顶部和尾部约束.
  • 前导和尾随到中间标签.
  • 前导、底部、尾随至底部标签.

这是另一个奇怪的部分.当我第一次运行它时,底部标签的裁剪问题仍然存在.

在此处输入图像描述

但如果我将设备旋转到横向,并将其转回到纵向,所有单元格的大小都会正确调整,以适应两个标签!

在此处输入图像描述

但我还是不明白为什么一开始不会发生这种事.更新的Xcode项目是here.

推荐答案

这里的问题是多行标签的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)

Swift相关问答推荐

解析SON数据的API调用有什么问题?

如何把多个可观测性变成一个完备性?

我在SwiftUI中的动画无法正常工作

如何在SwiftUI中更改NSTextView中的 Select colored颜色 ?

本地对象的异步/等待引用捕获总是安全的还是理论上不安全?

可以使 Swift init 仅可用于 Objective C 吗?

Swift 数组拆分成重叠的块

确定路径是否相交的有效方法

从 Swift Vapor Server 中调用 ShazamKit

SwiftUI 动画的范围

当使用 CGFloat 而不是数字文字时,为什么 node 的位置会发生变化?

按钮图像不会立即刷新

如何根据 SwiftUI 中的字体大小定义按钮的大小?

如何将使用\u{ea12}创建的 Swift 字符串转换回其十六进制值/字符串ea12?

为什么 `map(foo)` 和 `map{ foo($0) }` 返回不同的结果?

组合 flatMap 不会返回预期的上下文结果类型

仅在 Swift 中创建 Setter

在swift 3中将文件保存在文档目录中?

在 Swift 中子类化 NSObject - 初始化器的最佳实践

Swift - 要求实现协议的类是某个类的子类