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

Posted

技术标签:

【中文标题】使用自动布局约束动态调整表格视图单元格的大小【英文标题】:Dynamically size Table View Cells using Auto Layout constraints 【发布时间】:2015-05-06 13:29:22 【问题描述】:

更新

根据我的最新发现,我已经彻底修改了这个问题。

目标

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

    有一个简单的表格视图 用户选择了一行 所选行展开,在原始标签下方显示另一个标签

请注意,我知道,这可以通过在所选单元格I already have a successful implementation using that method 下方插入/删除单元格来实现。

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

当前状态

我有一个sample project available for anyone to check,还有一个opened an issue。总而言之,这是我迄今为止尝试过的:

作为演员,我有以下看法:

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

我已经通过以下方式在我的单元格中设置了自动布局约束(请注意,这是严格的伪语言):

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 子类,有多个出口,但这里最重要的是前面提到的高度约束的出口:这里的想法是默认将其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()
    

我有以下结果:

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

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

初始状态正常。内容视图的高度等于我的主视图的高度(在本例中为 125 磅)。

当单元格被选中时,详细视图的高度限制增加到 44 点,两个视图垂直堆叠。但不是单元格的内容视图扩展,而是主视图缩小。

问题

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

主视图的高度,当详细视图的高度约束为 0 时(目前有效) 当详细视图的高度约束设置正确时,主视图高度 + 详细视图高度(这不起作用)。

我必须如何设置约束才能实现这一目标?

【问题讨论】:

【参考方案1】:

经过大量研究,我想我在this great article.的帮助下找到了解决方案

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

在主视图和详细视图中,我最初将标签设置为水平和垂直居中。这对于自行调整大小的单元格是不够的。我需要做的第一件事是使用垂直间距约束而不是简单对齐来设置布局:

此外,您应该将主容器的垂直压缩阻力设置为 1000。

详细视图有点棘手:除了创建适当的垂直约束外,您还必须玩弄它们的优先级以达到预期的效果:

详细信息容器的高度被限制为 44 磅,但要使其成为可选,请将其优先级设置为 999(根据文档,低于“必需”的任何内容都将被视为此类)。 在 Detail Container 中,设置垂直间距约束,并赋予它们 998 的优先级。

主要思路如下:

默认情况下,单元格是折叠的。为此,我们必须以编程方式将 Detail Container 的高度约束的常量设置为 0。由于其优先级高于单元格内容视图中的垂直约束,后者将被忽略,因此 Detail Container 将被隐藏。李> 当我们选择单元格时,我们希望它展开。这意味着,必须控制垂直约束:我们将优先级 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

结果如下:

单元格现在可以正确调整自己的大小。您可能会注意到动画仍然有点奇怪,但修复不属于本问题的范围。

结论:这比它应该做的要难得多。 ? 我真的希望在未来看到一些改进。

【讨论】:

【参考方案2】:

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

添加您的viewDidLoad:

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

这将为您的 tableView 启用自动调整单元格大小,并且应该适用于 ios8+

【讨论】:

感谢您的发帖。不幸的是,这已经是我尝试过的方法,它本身并不能真正解决问题。

以上是关于使用自动布局约束动态调整表格视图单元格的大小的主要内容,如果未能解决你的问题,请参考以下文章

具有动态大小的单元格的复杂自动布局

自动调整大小(动态高度)表格视图单元格的问题

在具有自动布局的 UITableViewCell 中动态调整大小的 UIWebView - 违反约束

如何通过自动布局设置集合视图单元格大小

使用自动布局自定义集合视图单元格的动态高度

调整单元格大小时如何使表格视图动画平滑