[英]UITableViewCell doesn't update height after adding a view to UIStackView
I have a UIStackView
inside UITableViewCell
's contentView
. 我在
UITableViewCell
的contentView
有一个UIStackView
。 Based on user interaction, I add/remove items in the UIStackView
. 根据用户交互,我在
UIStackView
添加/删除项目。 After modifying the items in UIStackView
, I expect the cell
to update it's height accordingly. 修改
UIStackView
的项目后,我希望cell
地更新它的高度。 But, it doesn't update it's height unless I call tableView.reloadData()
. 但是,除非我调用
tableView.reloadData()
否则它不会更新它的高度。 But, calling reloadData()
in cellForRowAtIndexPath
/ willDisplayCell
becomes recursive. 但是,在
cellForRowAtIndexPath
/ willDisplayCell
调用reloadData()
会变为递归。
What is the proper way to adjust the cell
height at run time based on items in UIStackView
? 根据
UIStackView
项目,在运行时调整cell
高度的正确方法是什么?
I use UITableViewAutomaticDimension
我使用
UITableViewAutomaticDimension
Updating the Problem: 更新问题:
Here is a simple prototype of what I am trying to do. 这是我想要做的简单原型。
My actual problem is dequeuing the cell. 我的实际问题是将单元格出列。
In the prototype, I have 2 reusable cells and 3 rows. 在原型中,我有2个可重复使用的单元格和3行。 For row 0 and 2, I dequeue
cellA
and for row 1, I dequeue cellB
. 对于第0行和第2行,我使
cellA
出cellA
,对于第1行,我使cellB
出列cellB
。 Below is the overview on the condition I use. 以下是我使用的条件的概述。
if indexPath.row == 0 {
// dequeue cellA with 2 items in stackView
}
if indexPath.row == 1 {
// dequeue cellB with 25 items in stackView
}
if indexPath.row == 2 {
// dequeue cellA with 8 items in stackView
}
But the output is, row 0 contains 2 items in stackView
- expected row 1 contains 25 items in stackView
- expected row 2 contains 2 items in stackView
- unexpected, row 0 is dequeued 但输出是,第0行包含
stackView
中的2个项目 - 预期第1行包含stackView
中的25个项目 - 预期第2行包含stackView
中的2个项目 - 意外,第0行已出列
I also tried removing all arranged subViews of stackView
in cellForRowAtIndexPath
. 我还尝试在
cellForRowAtIndexPath
删除所有已安排的stackView子stackView
。 But, doing so, flickers the UI when scrolling. 但是,这样做会在滚动时闪烁UI。 How can I manage to get the desired output?
如何设法获得所需的输出?
Try to reload only the cell using: https://developer.apple.com/documentation/uikit/uitableview/1614935-reloadrows 尝试使用以下命令仅重新加载单元格: https : //developer.apple.com/documentation/uikit/uitableview/1614935-reloadrows
Example code 示例代码
Here is an example. 这是一个例子。 We have basic table view cells (TableViewCell) inside a view controller.
我们在视图控制器中有基本的表视图单元(TableViewCell)。 The cells have 2 labels inside a stack view.
单元格在堆栈视图中有2个标签。 We can hide or show the second label using the collapse/reveal methods.
我们可以使用collapse / reveal方法隐藏或显示第二个标签。
class TableViewCell : UITableViewCell {
@IBOutlet private var stackView: UIStackView!
@IBOutlet private var firstLabel: UILabel!
@IBOutlet private var secondLabel: UILabel!
func collapse() {
secondLabel.isHidden = true
}
func reveal() {
secondLabel.isHidden = false
}
}
class ViewController : UIViewController {
@IBOutlet var tableView: UITableView!
fileprivate var collapsedCells: Set<IndexPath> = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 128
}
@IBAction private func buttonAction(_ sender: Any) {
collapseCell(at: IndexPath(row: 0, section: 0))
}
private func collapseCell(at indexPath: IndexPath) {
if collapsedCells.contains(indexPath) {
collapsedCells.remove(indexPath)
} else {
collapsedCells.insert(indexPath)
}
tableView.reloadRows(at: [indexPath], with: .automatic)
}
}
extension ViewController : UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell
if collapsedCells.contains(indexPath) {
cell.collapse()
} else {
cell.reveal()
}
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
}
I believe the problem is when you are adding views to the stack view. 我相信问题是当您向堆栈视图添加视图时。
In general, adding elements should take place when the cell is initialized . 通常,在初始化单元格时应该添加元素。
willDisplay cell:
is where one handles modifying attributes of cell contents. willDisplay cell:
是处理修改单元格内容属性的地方。
If you move your code from willDisplay cell:
to cellForRowAt indexPath:
you should see a big difference. 如果您将代码从
willDisplay cell:
移动到cellForRowAt indexPath:
您应该看到一个很大的区别。
I just made that one change to the code you linked to, and the rows are now auto-sizing based on the stack view contents. 我刚刚对您链接的代码进行了一次更改,现在行根据堆栈视图内容自动调整大小。
Edit: Looked at your updated code... the issue was still that you are adding your arrangedSubviews in the wrong place. 编辑:查看更新后的代码...问题仍然是您将arrangeSubviews添加到错误的位置。 And you compound it by calling reloadData().
然后通过调用reloadData()来复合它。
Second Edit: Forgot to handle previously added subviews when the cells are reused. 第二次编辑:忘记在重复使用单元格时处理以前添加的子视图。
Updated code... replace your ViewController code with: 更新代码...将ViewController代码替换为:
//
// ViewController.swift
//
import UIKit
class ViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView()
tableView.estimatedRowHeight = 56
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = UITableViewCell()
if indexPath.row == 0 || indexPath.row == 2 {
cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
if let stackView = cell.viewWithTag(999) as? UIStackView {
let numberOfItemsInStackView = (indexPath.row == 0) ? 2 : 8
let color = (indexPath.row == 0) ? UIColor.gray : UIColor.black
// cells are reused, so clear out any previously added subviews...
// but leave the first view that is part of the cell prototype
while stackView.arrangedSubviews.count > 1 {
stackView.arrangedSubviews[1].removeFromSuperview()
}
// use "i" so we can count
for i in 1...numberOfItemsInStackView {
// use label instead of view so we can number them for testing
let newView = UILabel()
newView.text = "\(i)"
newView.textColor = .yellow
// add a border, so we can see the frames
newView.layer.borderWidth = 1.0
newView.layer.borderColor = UIColor.red.cgColor
newView.backgroundColor = color
let heightConstraint = newView.heightAnchor.constraint(equalToConstant: 54)
heightConstraint.priority = 999
heightConstraint.isActive = true
stackView.addArrangedSubview(newView)
}
}
}
if indexPath.row == 1 {
cell = tableView.dequeueReusableCell(withIdentifier: "lastCell")!
if let stackView = cell.viewWithTag(999) as? UIStackView {
let numberOfItemsInStackView = 25
// cells are reused, so clear out any previously added subviews...
// but leave the first view that is part of the cell prototype
while stackView.arrangedSubviews.count > 1 {
stackView.arrangedSubviews[1].removeFromSuperview()
}
// use "i" so we can count
for i in 1...numberOfItemsInStackView {
// use label instead of view so we can number them for testing
let newView = UILabel()
newView.text = "\(i)"
newView.textColor = .yellow
// add a border, so we can see the frames
newView.layer.borderWidth = 1.0
newView.layer.borderColor = UIColor.red.cgColor
newView.backgroundColor = UIColor.darkGray
let heightConstraint = newView.heightAnchor.constraint(equalToConstant: 32)
heightConstraint.priority = 999
heightConstraint.isActive = true
stackView.addArrangedSubview(newView)
}
}
}
return cell
}
// override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
// if cell.reuseIdentifier == "cell" {
// if let stackView = cell.viewWithTag(999) as? UIStackView {
// let numberOfItemsInStackView = (indexPath.row == 0) ? 2 : 8
// let color = (indexPath.row == 0) ? UIColor.gray : UIColor.black
// guard stackView.arrangedSubviews.count == 1 else { return }
// for _ in 1...numberOfItemsInStackView {
// let newView = UIView()
// newView.backgroundColor = color
// let heightConstraint = newView.heightAnchor.constraint(equalToConstant: 54)
// heightConstraint.priority = 999
// heightConstraint.isActive = true
// stackView.addArrangedSubview(newView)
// }
// tableView.reloadData()
// }
// }
//
// if cell.reuseIdentifier == "lastCell" {
// if let stackView = cell.viewWithTag(999) as? UIStackView {
// let numberOfItemsInStackView = 25
// guard stackView.arrangedSubviews.count == 1 else { return }
// for _ in 1...numberOfItemsInStackView {
// let newView = UIView()
// newView.backgroundColor = UIColor.darkGray
// let heightConstraint = newView.heightAnchor.constraint(equalToConstant: 32)
// heightConstraint.priority = 999
// heightConstraint.isActive = true
// stackView.addArrangedSubview(newView)
// }
// tableView.reloadData()
// }
// }
// }
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.