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

14

更新

我在最新的调查发现后完全修改了问题。

目标

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

  1. 有一个简单的表格视图
  2. 用户选择一行
  3. 所选行展开,下面出现另一个标签

请注意,我知道可以通过在所选的行下方插入/删除单元格来实现此目的,我已经成功使用该方法进行了实现

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

当前状态

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

这里有以下视图:

  • 单元格的内容视图
  • 顶部视图,包含主标签 ("main view")
  • 底部视图,在主视图下方,包含最初隐藏的标签 ("detail view")

我已经在我的单元格内设置了自动布局约束(请注意,这是严格的伪代码):

  • 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()
    }
}

我得到了以下结果:

Current status

所以这个效果是有效的,但不完全符合我的最初想法。我想让单元格的高度在显示详细视图时增加,并在隐藏详细视图时恢复原状

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

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

Screenshot initial

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

Screenshot detailed

问题

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

  • 当详细视图的高度约束为0时,主视图的高度(当前有效)
  • 当详细视图的高度约束正确设置时,主视图高度+详细视图高度(当前无效)。

我该如何设置约束以实现这一点?

2个回答

13

通过广泛的研究,我认为在这篇优秀的文章的帮助下,我已经找到了解决方案。

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

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

Main View

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

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

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

Detail View

主要思想如下:

  • 默认情况下,单元格被折叠。为了实现这一点,我们必须以编程方式将详细容器的高度约束常量设置为0。由于其优先级高于单元格内容视图内的垂直约束,后者将被忽略,因此详细容器将被隐藏。
  • 当我们选择单元格时,我们希望它展开。这意味着垂直约束必须控制:我们将详细容器的高度约束优先级设置为较低的值(我使用了250),因此它将被忽略,以 favor 单元格内容视图内的约束。

我不得不修改我的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
}

这是结果:

结果

单元格现在可以正确地自适应大小。你可能会注意到动画仍然有点奇怪,但修复这个问题不属于本问题的范围。

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


-1

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

viewDidLoad:中添加:

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

这将启用自适应单元格大小以适应tableView,并应该适用于iOS8+。


感谢您的发帖。不幸的是,这已经是我尝试过的东西,它本身并不能解决问题。 - József Vesza

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接