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

2024-01-29

Update

根据我的最新发现,我完全修改了这个问题。

Goal

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

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

请注意,我知道,这可以通过在所选单元格下方插入/删除单元格来实现,我已经使用该方法成功实现了 https://github.com/jozsef-vesza/TableViewExpansions.git.

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

当前状态

我有一个示例项目可供任何人检查 https://github.com/jozsef-vesza/ExpandableTableView.git,并且提出了一个问题 https://github.com/jozsef-vesza/ExpandableTableView/issues/1。总而言之,这是我迄今为止尝试过的:

作为演员,我有以下观点:

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

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

  • mainView.top = contentView.top
  • mainView.leading = contentView.leading
  • mainView.trailing = contentView.trailing
  • mainView.bottom=detailView.top
  • 详细信息视图.领先 = 内容视图.领先
  • 详细信息视图.尾随 = 内容视图.尾随
  • 详细信息视图.底部 = 内容视图.底部
  • 细节视图.高度 = 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()
    }
}

我有以下结果:

所以效果是有效的,但并不完全是我最初想象的那样。我不希望将主视图向上推,而是希望单元格的高度grow当显示详细视图时,以及退缩当它被隐藏时。

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

  • 初始状态是OK的。内容视图的高度等于我的主视图的高度(在本例中为 125 点)。
  • 当单元格被选中时,详细视图的高度约束增加到 44 点,两个视图正确地垂直堆叠。但是单元格的内容视图不是延伸,而是主视图缩小。

Question

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

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

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


经过大量研究后,我想我已经在以下帮助下找到了解决方案这篇很棒的文章。 http://pivotallabs.com/expandable-uitableviewcells/

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

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

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

细节视图有点棘手:除了创建适当的垂直约束之外,您还必须调整它们的优先级以达到所需的效果:

  • 详细信息容器的高度被限制为 44 点,但为了使其可选,请将其优先级设置为 999(根据文档,任何低于“必需”的值都将被视为如此)。
  • 在详细信息容器中,设置垂直间距约束,并为其指定优先级 998。

主要思想如下:

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

结果如下:

细胞现在可以正确调整自身大小。您可能会注意到动画仍然有点奇怪,但是解决这个问题不属于这个问题的范围。

结论:这比应有的要困难得多。 ????我真的希望未来能看到一些改进。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐