简体   繁体   English

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

[英]Dynamically size Table View Cells using Auto Layout constraints

Update 更新

I have revised the question completely after my latest findings. 在我的最新发现之后,我完全修改了这个问题。

Goal 目标

My goal is to implement the following effect: 我的目标是实现以下效果:

  1. There is a simple table view 有一个简单的表格视图
  2. The user selects a row 用户选择一行
  3. The selected row expands, revealing another label below the original one 选定的行会展开,显示原始行下方的另一个标签

Please note that I am aware, that this can be achieved by inserting/deleting cells below the selected one, I already have a successful implementation using that method . 请注意,我知道,这可以通过插入/删除所选单元格下方的单元格来实现, 我已经使用该方法成功实现了

This time, I want to try to achieve this using Auto Layout constraints. 这次,我想尝试使用Auto Layout约束来实现这一点。

Current status 当前状态

I have a sample project available for anyone to check , and also opened an issue . 我有一个示例项目可供任何人查看 ,也打开了一个问题 To summarize, here's what I've tried so far: 总结一下,这是我到目前为止所尝试的内容:

I have the following views as actors here: 我在这里作为演员有以下观点:

  • The cell's content view 单元格的内容视图
  • A top view, containing the main label ("main view") 顶视图,包含主标签(“主视图”)
  • A bottom view, below the main view, containing the initially hidden label ("detail view") 主视图下方的底视图,包含最初隐藏的标签(“详细视图”)

I have set up Auto Layout constraints within my cell the following way (please note that this is strictly pseudo-language): 我已经按照以下方式在我的单元格中设置了自动布局约束(请注意,这是严格的伪语言):

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

I have a custom UITableViewCell subclass, with multiple outlets, but the most important here is an outlet for the height constraint mentioned previously: the idea here is to set its constant to 0 by default, but when the cell is selected, set it to 44, so it becomes visible: 我有一个自定义的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()
    }
}

I have the following result: 我有以下结果:

当前状态

So the effect is working, but not exactly how I originally imagined. 所以效果是有效的,但不完全是我最初的想象。 Instead of pushing the main view up, I want the cell's height to grow when the detail view is shown, and shrink back when it's hidden. 我没有推动主视图,而是希望细胞的高度在显示细节视图时增长 ,并在隐藏时收缩

I have examined my layout hierarchy during runtime: 我在运行时检查了我的布局层次结构:

  • The initial state is OK. 初始状态还可以。 The height of the content view is equal to the height of my main view (in this case, it's 125 points). 内容视图的高度等于我的主视图的高度(在这种情况下,它是125点)。

截图初始

  • When the cell is selected, the height constraint of the detail view is increased to 44 points and the two views are properly stacked vertically.But instead of the cell's content view extending, but instead, the main view shrinks. 选择单元格后,细节视图的高度约束将增加到44个点,并且两个视图将垂直堆叠。但不是单元格的内容视图扩展,而是主视图缩小。

截图详细

Question

What I need is the following: the height of table view cell's content view should be equal to 我需要的是:表格视图单元格内容视图的高度应该等于

  • the height of the main view, when the detail view's height constraint is 0 (currently this works) 当详细视图的高度约束为0时,主视图的高度(当前这是有效的)
  • main view height + detail view height when the detail view's height constraint is set properly (this does not work). 正确设置详细视图的高度约束时的主视图高度+详细视图高度(这不起作用)。

How do I have to set my constraints to achieve that? 我如何设置约束来实现这一目标?

After a significant amount of research, I think I've found the solution with the help of this great article. 经过大量的研究,我想我已经在这篇伟大的文章的帮助下找到了解决方案

Here are the steps needed to make the cell resize: 以下是使单元格调整大小所需的步骤:

Within the Main, and Detail Views, I have originally set the labels to be horizontally and vertically centered. 在Main和Detail Views中,我最初将标签设置为水平和垂直居中。 This isn't enough for self sizing cells. 这对于自我调整细胞是不够的。 The first thing I needed is to set up my layout using vertical spacing constraints instead of simple alignment: 我需要的第一件事是使用垂直间距约束而不是简单对齐来设置我的布局:

主视图

Additionally you should set the Main Container's vertical compression resistance to 1000. 此外,您应将主容器的垂直压缩阻力设置为1000。

The detail view is a bit more tricky: Apart from creating the appropriate vertical constraints, you also have to play with their priorities to reach the desired effect: 细节视图有点棘手:除了创建适当的垂直约束外,您还必须使用它们的优先级来达到所需的效果:

  • The Detail Container's Height is constrained to be 44 points, but to make it optional, set its priority to 999 (according to the docs, anything lower than "Required", will be regarded such). 细节容器的高度被限制为44点,但要使其成为可选项,请将其优先级设置为999(根据文档,任何低于“必需”的内容都将被视为此类)。
  • Within the Detail Container, set up the vertical spacing constraints, and give them a priority of 998. 在Detail Container中,设置垂直间距约束,并为它们指定优先级998。

详细视图

The main idea is the following: 主要想法如下:

  • By default, the cell is collapsed. 默认情况下,单元格已折叠。 To achieve this, we must programmatically set the constant of the Detail Container's height constraint to 0. Since its priority is higher than the vertical constraints within the cell's content view, the latter will be ignored, so the Detail Container will be hidden. 为此,我们必须以编程方式将Detail Container的高度约束的常量设置为0.由于其优先级高于单元格内容视图中的垂直约束,后者将被忽略,因此将隐藏详细信息容器。
  • When we select the cell, we want it to expand. 当我们选择单元格时,我们希望它扩展。 This means, that the vertical constraints must take control: we set the priority Detail Container's height constraint to something low (I used 250), so it will be ignored in favor of the constraints within the content view. 这意味着,垂直约束必须采取控制:我们将优先级Detail Container的高度约束设置为低(我使用250),因此将忽略它​​以支持内容视图中的约束。

I had to modify my UITableViewCell subclass to support these operations: 我不得不修改我的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
}

To trigger the behavior, we must override tableView(_:didSelectRowAtIndexPath:) : 要触发行为,我们必须覆盖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
    }
}

Notice that I've introduced expandedIndexPath to keep track of our currently expanded index: 请注意,我已经引入了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)
        }
    }
}

Setting the property will result in the table view reloading the appropriate indexes, giving us a perfect opportunity to tell the cell, if it should expand: 设置属性将导致表视图重新加载适当的索引,为我们提供了一个告诉单元格的完美机会,是否应该展开:

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
}

The last step is to enable self-sizing in viewDidLoad() : 最后一步是在viewDidLoad()启用自我调整大小:

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

Here is the result: 结果如下:

结果

Cells now correctly size themselves. 细胞现在可以自己调整大小 You may notice that the animation is still a bit weird, but fixing that does not fall into the scope of this question. 你可能会注意到动画仍然有点奇怪,但修复它不属于这个问题的范围。

Conclusion: this was way harder than it should be. 结论:这比应该的更难。 😀 I really hope to see some improvements in the future. 😀我真的希望看到未来会有一些改进。

This is in obj-c, but I'm sure you'll handle that: 这是obj-c,但我相信你会处理它:

Add in your viewDidLoad: viewDidLoad:添加viewDidLoad:

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

This will enable self sizing cells for your tableView, and should work on iOS8+ 这将为tableView启用自定义单元格,并且应该可以在iOS8 +上运行

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用自动布局在动态表格视图单元格中出现奇怪的填充 - Odd Padding Appears in Dynamic Table View Cells Using Auto Layout 如何使用“自动布局”约束将2个具有可变大小的标签放入表格单元格中,该表格单元格的高度是使用EstimateHeightForRowAtIndexPath估算的 - How to use Auto Layout constraints to fit 2 labels with variable size in a table cell which height is estimated using estimatedHeightForRowAtIndexPath 具有多个动态高度的表格视图上的Xcode自动布局垂直约束 - Xcode Auto Layout Vertical Constraints on Table View with Multiple Dynamic Heights 使用“自动布局”更新集合视图高度以适合表视图单元格中的所有单元格 - Update collection view height to fit all cells within a table view cell using Auto Layout 动态视图的自动布局约束 - Auto layout constraints for dynamic view 如何使用自动布局约束来调整和定位嵌入式 UIViewController? - How to size and position an embedded UIViewController using Auto Layout constraints? 如何使用自动布局动态添加视图 - How to Dynamically add a View using Auto Layout 在自定义视图中使用自动布局,其中约束依赖于框架 - Using auto-layout in a custom view where constraints rely on frame 使用自动布局时视图没有约束时会发生什么? - What happens when a view has no constraints when using Auto Layout? systemLayoutSizeFitting忽略布局约束并使用图像视图固有尺寸 - systemLayoutSizeFitting ignoring layout constraints and using image view intrinsic size
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM