将视图添加到 UIStackView 后 UITableViewCell 不更新高度
Posted
技术标签:
【中文标题】将视图添加到 UIStackView 后 UITableViewCell 不更新高度【英文标题】:UITableViewCell doesn't update height after adding a view to UIStackView 【发布时间】:2017-06-28 10:10:45 【问题描述】:我在UITableViewCell
的contentView
中有一个UIStackView
。基于用户交互,我在UIStackView
中添加/删除项目。修改UIStackView
中的项目后,我希望cell
相应地更新它的高度。但是,除非我打电话给tableView.reloadData()
,否则它不会更新它的高度。但是,在 cellForRowAtIndexPath
/ willDisplayCell
中调用 reloadData()
会变得递归。
在运行时根据UIStackView
中的项目调整cell
高度的正确方法是什么?
我用UITableViewAutomaticDimension
更新问题:
Here 是我正在尝试做的一个简单原型。
我的实际问题是使单元格出队。
在原型中,我有 2 个可重复使用的单元格和 3 行。对于第 0 行和第 2 行,我将 cellA
出队,对于第 1 行,我将 cellB
出队。以下是我使用的条件的概述。
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
但输出是,
第 0 行包含 stackView
中的 2 个项目 - 预期
第 1 行包含 stackView
中的 25 个项目 - 预期
第 2 行包含 stackView
中的 2 项 - 意外,第 0 行已出列
我还尝试删除stackView
在cellForRowAtIndexPath
中的所有排列的子视图。但是,这样做会在滚动时闪烁 UI。我怎样才能获得所需的输出?
【问题讨论】:
您是否对 StackView 的 ContentView 应用了前导、尾随、顶部、底部约束? 是的..请检查所附来源 【参考方案1】:我认为问题在于当您将视图添加到堆栈视图中。
一般来说,添加元素应该在单元格初始化时进行。
willDisplay cell:
是处理修改单元格内容属性的地方。
如果您将代码从 willDisplay cell:
移动到 cellForRowAt indexPath:
,您应该会看到很大的不同。
我刚刚对您链接的代码进行了一次更改,现在这些行会根据堆栈视图内容自动调整大小。
编辑:查看了您更新的代码...问题仍然是您将排列的子视图添加到错误的位置。你可以通过调用 reloadData() 来复合它。
第二次编辑:重复使用单元格时忘记处理之前添加的子视图。
更新的代码...将您的 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
【讨论】:
我已经更新了我面临的问题。我面临的实际问题是使单元出队。我还更新了定义问题的来源 感谢您更新代码。我在原型中使用了您的代码。它有一个出队问题。第三行预计有 8 个项目,但它只包含 2 个 是的 - 我稍微改变了大小,所以我可以同时看到所有三行......这导致我没有注意到当单元格被重用时我忘记处理删除以前添加的视图.请参阅我的“第二次编辑”代码。【参考方案2】:尝试仅使用以下方式重新加载单元格:https://developer.apple.com/documentation/uikit/uitableview/1614935-reloadrows
示例代码
这是一个例子。我们在视图控制器中有基本的表格视图单元(TableViewCell)。单元格在堆栈视图中有 2 个标签。我们可以使用折叠/显示方法隐藏或显示第二个标签。
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
【讨论】:
我已附上问题的代码。我已经在我的代码中注释了reloadRowsAtIndexPaths
行。只需取消注释并检查应用的行为
@ios 我加了一个完整的例子
我已经更新了我面临的问题。我面临的实际问题是使单元出队。我还更新了定义问题的来源以上是关于将视图添加到 UIStackView 后 UITableViewCell 不更新高度的主要内容,如果未能解决你的问题,请参考以下文章
如何将 UIImageViews 添加到受限于视图的 centerXAnchor 的 UIStackView?
将 uipageviewcontroller 添加到 uistackview