使用自动布局约束dynamic调整表格视图单元格大小

更新

我最新的发现后,我已经完全修改了这个问题。

目标

我的目标是实现以下效果:

  1. 有一个简单的表格视图
  2. 用户select一行
  3. 选定的行将展开,显示另一个标签下方的原始标签

请注意,我知道,这可以通过插入/删除单元格下面的单元格, 我已经有一个成功的实现使用该方法 。

这一次,我想尝试使用自动布局约束来实现这一点。

当前状态

我有一个示例项目供任何人检查 ,并且还打开了一个问题 。 总结一下,以下是我迄今为止所尝试的:

我在这里有以下观点作为演员:

  • 单元格的内容视图
  • 包含主标签(“主视图”)的顶视图
  • 底视图,在主视图下方,包含最初隐藏的标签(“详细视图”)

我已经在我的单元格中设置了自动布局约束(请注意,这完全是伪语言):

  • mainView.top = contentView.top
  • mainView.leading = contentView.leading
  • mainView.trailing = contentView.trailing
  • mainView.bottom = detailView.top
  • detailView.leading = contentView.leading
  • detailView.trailing = contentView.trailing
  • detailView.bottom = contentView.bottom
  • detailView.height = 0

我有一个自定义的UITableViewCell子类,有多个sockets,但是这里最重要的是前面提到的高度约束的一个sockets:这里的想法是默认将其constant设置为0,但是当选中该单元时,将其设置为44 ,所以它变得可见:

 override func setSelected(selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) detailViewHeightConstraint.constant = selected ? detailViewDefaultHeight : 0 UIView.animateWithDuration(0.3) { self.layoutIfNeeded() } } 

我有以下结果:

当前状态

所以效果是有效的,但不是我原本想象的。 而不是将主视图向上推,我希望单元格的高度在显示详细视图时增长 ,隐藏时缩小

我在运行时检查了布局层次结构:

  • 初始状态是OK。 内容视图的高度等于我的主视图的高度(在这种情况下,它是125点)。

截图初始

  • 当单元格被选中时,细节视图的高度约束被增加到44点,并且两个视图被垂直地正确堆叠。而不是单元格的内容视图扩展,而是主视图缩小。

详细的截图

我需要的是:表格视图单元格的内容视图的高度应该等于

  • 主视图的高度,当细节视图的高度约束为0(目前这个工作)
  • 主视图的高度+细节视图的高度,当细节视图的高度约束设置正确(这是行不通的)。

我如何设置我的约束来实现呢?

经过大量的研究,我想我已经find了这个伟大的文章的帮助下的解决scheme。

以下是进行单元格大小调整所需的步骤:

在Main和Detail Views中,我最初将标签设置为水平和垂直居中。 这对于自我测量的细胞是不够的。 我需要的第一件事是设置我的布局使用垂直间距约束,而不是简单的alignment方式:

主视图

另外,您应该将主容器的垂直抗压强度设置为1000。

细节视图有点棘手:除了创build适当的垂直约束之外,您还必须发挥其优先级来达到预期的效果:

  • 详细容器的高度限制为44点,但是为了使其可选,将其优先级设置为999(根据文档,低于“必需”的任何东西都将被视为这样)。
  • 在Detail Container中,设置垂直间距约束,并赋予它们998的优先级。

详细视图

主要想法如下:

  • 默认情况下,单元格已折叠。 为了达到这个目的,我们必须以编程方式将Detail Container的高度约束的常量设置为0.由于其优先级高于单元格内容视图中的垂直约束,后者将被忽略,因此Detail Container将被隐藏。
  • 当我们select细胞时,我们希望它扩大。 这意味着,垂直约束必须控制:我们将优先级Detail Container的高度约束设置为低(我使用了250),所以在内容视图中将被忽略。

我不得不修改我的UITableViewCell子类来支持这些操作:

 // `showDetails` is exposed to control, whether the cell should be expanded var showsDetails = false { didSet { detailViewHeightConstraint.priority = showsDetails ? lowLayoutPriority : highLayoutPriority } } override func awakeFromNib() { super.awakeFromNib() detailViewHeightConstraint.constant = 0 } 

要触发行为,我们必须重写tableView(_:didSelectRowAtIndexPath:)

 override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { tableView.deselectRowAtIndexPath(indexPath, animated: false) switch expandedIndexPath { case .Some(_) where expandedIndexPath == indexPath: expandedIndexPath = nil case .Some(let expandedIndex) where expandedIndex != indexPath: expandedIndexPath = nil self.tableView(tableView, didSelectRowAtIndexPath: indexPath) default: expandedIndexPath = indexPath } } 

请注意,我已经引入了expandedIndexPath来跟踪我们当前的扩展索引:

 var expandedIndexPath: NSIndexPath? { didSet { switch expandedIndexPath { case .Some(let index): tableView.reloadRowsAtIndexPaths([index], withRowAnimation: UITableViewRowAnimation.Automatic) case .None: tableView.reloadRowsAtIndexPaths([oldValue!], withRowAnimation: UITableViewRowAnimation.Automatic) } } } 

设置属性将导致表视图重新加载适当的索引,给我们一个很好的机会告诉单元格是否应该扩展:

 override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! ExpandableTableViewCell cell.mainTitle = viewModel.mainTitleForRow(indexPath.row) cell.detailTitle = viewModel.detailTitleForRow(indexPath.row) switch expandedIndexPath { case .Some(let expandedIndexPath) where expandedIndexPath == indexPath: cell.showsDetails = true default: cell.showsDetails = false } return cell } 

最后一步是在viewDidLoad()启用自定义大小:

 override func viewDidLoad() { super.viewDidLoad() tableView.contentInset.top = statusbarHeight tableView.rowHeight = UITableViewAutomaticDimension tableView.estimatedRowHeight = 125 } 

结果如下:

结果

单元现在可以正确resize 您可能会注意到,animation还是有些奇怪,但修复不属于这个问题的范围。

结论:这比应该更难。 😀我真的希望在未来看到一些改进。

这是obj-c,但我相信你会处理:

添加你的viewDidLoad:

self.tableView.estimatedRowHeight = self.tableView.rowHeight; self.tableView.rowHeight = UITableViewAutomaticDimension;

这将启用自定义单元格为您的tableView,并应该在iOS8 +上工作