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

时间:2015-05-06 13:29:22

标签: ios uitableview autolayout ios-autolayout

更新

我在最新调查结果后完全修改了这个问题。

目标

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

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

    请注意,我知道,这可以通过插入/删除所选单元格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()
        }
    }
    

    我有以下结果:

    Current status

    所以效果是有效的,但不是我最初的想象。我不希望推出主视图,而是在显示详细视图时希望单元格的高度增长,而当隐藏它时,缩小

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

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

    Screenshot initial

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

    Screenshot detailed

    问题

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

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

    我如何设置约束来实现这一目标?

2 个答案:

答案 0 :(得分:13)

经过大量研究,我认为我已经在this great article.

的帮助下找到了解决方案

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

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

Main View

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

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

  • 详细信息容器的高度限制为44分,但要使其成为可选项,请将其优先级设置为999(根据文档,任何低于"必需"的任何内容都将被视为这样)。
  • 在详细信息容器中,设置垂直间距约束,并优先级为998。

Detail View

主要想法如下:

  • 默认情况下,单元格已折叠。为了实现这一点,我们必须以编程方式将Detail Container的高度约束的常量设置为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
}

结果如下:

Result

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

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

答案 1 :(得分:-1)

这是obj-c,但我确定你会处理这个问题:

添加viewDidLoad:

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

这将为tableView启用自定义单元格,并且应该可以在iOS8 +上运行