UITableviewCell 中的 UICollectionView 第一次给出错误的 contentSizeHeight 但在滚动后变为 true
UICollectionView inside UITableviewCell give wrong contentSizeHeight at first time but becomes true after scroll
我有一个结构如下的视图
-Tableview
- TableviewCell
- UICollectionView
- UICollectionViewCell
uicollectionview 单元格定义了一个带有 collectionViewFlowLayout 的左对齐标签
class Row {
var attributes = [UICollectionViewLayoutAttributes]()
var spacing: CGFloat = 0
init(spacing: CGFloat) {
self.spacing = spacing
}
func add(attribute: UICollectionViewLayoutAttributes) {
attributes.append(attribute)
}
func tagLayout(collectionViewWidth: CGFloat) {
let padding = 0
var offset = padding
for attribute in attributes {
attribute.frame.origin.x = CGFloat(offset)
offset += Int(attribute.frame.width + spacing)
}
}
}
class LeftAlignTagCollectionViewFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let attributes = super.layoutAttributesForElements(in: rect) else {
return nil
}
var rows = [Row]()
var currentRowY: CGFloat = -1
for attribute in attributes {
if currentRowY != attribute.frame.origin.y {
currentRowY = attribute.frame.origin.y
rows.append(Row(spacing: 4))
}
rows.last?.add(attribute: attribute)
}
rows.forEach {
[=11=].tagLayout(collectionViewWidth: collectionView?.frame.width ?? 0)
}
return rows.flatMap { [=11=].attributes }
}
}
这是 ViewController 中数据源 cellForRow 处处理 tableview
的代码
let cell = tableView.dequeueReusableCell(withIdentifier: "CollectionTableViewCell") as? CollectionTableViewCell
cell?.setupCell(showFirstItem: true, showSecondItem: true)
cell?.collectionView.reloadData()
cell?.layoutIfNeeded()
let height = cell?.collectionView.collectionViewLayout.collectionViewContentSize.height
cell?.collectionViewHeightConstraint.constant = height ?? 0
我已经在访问 collectionview 内容大小高度之前调用了 layoutIfNeeded() 并将内容大小高度分配给 collectionview 高度约束。我已经将 isScrollEnabled 设置为 false。
在 CollectionTableViewCell 中它看起来像这样
@IBOutlet weak var collectionView: UICollectionView!
@IBOutlet weak var collectionViewHeightConstraint: NSLayoutConstraint! // Required priority (1000)
private var collectionItem: [String] = []
override func awakeFromNib() {
super.awakeFromNib()
setupCollectionView()
collectionView.dataSource = self
collectionView.delegate = self
let layout = LeftAlignTagCollectionViewFlowLayout()
layout.estimatedItemSize = CGSize(width: 140, height: 40)
collectionView.collectionViewLayout = layout
}
func setupCell(showFirstItem: Bool, showSecondItem: Bool) {
setupCollectionItem()
}
private func setupCollectionItem(showFirstItem: Bool, showSecondItem: Bool) {
collectionItem = []
if showFirstItem {
let data = "TagNumber1"
collectionItem.append(data)
}
if showSecondItem {
let data = "TagNumber2"
collectionItem.append(data)
}
}
extension CollectionTableViewCell: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
private func setupCollectionView() {
collectionView.register(UINib(nibName: "CollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "CollectionViewCell")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return collectionItem.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as? CollectionViewCell
cell?.configCell(model: collectionItem[indexPath.row], maxWidth: collectionView.frame.width - 8)
cell?.setNeedsLayout()
cell?.layoutIfNeeded()
return cell ?? UICollectionViewCell()
}
}
总而言之,当我 运行 上面的代码时,它会在第一次加载时给出错误的内容大小高度让我们说大约 86.0 但是在第二次滚动或重新加载 tableview 之后(我有两次重新加载 tableview因为在这个 ViewController 中有两种调用 API 的方法,所以它给出了正确的数字,假设我想要的目标是 24.0,所以当从第一个值 86.0 更改为 24.0 时,它会在 uitableviewcell 中出现故障。在完成 collectionview 高度计算过程之前,我无法隐藏 tableview。另一点是,如果我在 collectionItem 中只有一个项目,它会给出正确的 contentsize 高度!但是当它变成两个项目时,问题就出现了。
等待您的建议,谢谢
这本质上是有问题的,有几个原因...
首先,集合视图有几个主要好处:
- 自动滚动
- 通过单元重用进行内存管理
在为“auto-size”集合视图编写代码时,这两个好处都被抛弃了。
其次,当table视图单元首次实例化时,它不知道它的实际宽度是多少 .因此,当集合视图对其单元格进行布局时,它会使用它 开始 的任何宽度——例如,如果它在 Storyboard 中设置为单元格原型,它将使用 Storyboard 中的宽度.
如果您搜索 uicollectionview inside uitableviewcell dynamic height
,您会发现许多讨论和尝试让它发挥作用,并取得了不同程度的成功。
但是,因为 - 如上所述 - 您没有使用 集合视图的两个“最佳功能”,您可能需要采用不同的方法。
看看这个对类似“标签布局”问题的回答。您可能会发现更容易使用:
我有一个结构如下的视图
-Tableview
- TableviewCell
- UICollectionView
- UICollectionViewCell
uicollectionview 单元格定义了一个带有 collectionViewFlowLayout 的左对齐标签
class Row {
var attributes = [UICollectionViewLayoutAttributes]()
var spacing: CGFloat = 0
init(spacing: CGFloat) {
self.spacing = spacing
}
func add(attribute: UICollectionViewLayoutAttributes) {
attributes.append(attribute)
}
func tagLayout(collectionViewWidth: CGFloat) {
let padding = 0
var offset = padding
for attribute in attributes {
attribute.frame.origin.x = CGFloat(offset)
offset += Int(attribute.frame.width + spacing)
}
}
}
class LeftAlignTagCollectionViewFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let attributes = super.layoutAttributesForElements(in: rect) else {
return nil
}
var rows = [Row]()
var currentRowY: CGFloat = -1
for attribute in attributes {
if currentRowY != attribute.frame.origin.y {
currentRowY = attribute.frame.origin.y
rows.append(Row(spacing: 4))
}
rows.last?.add(attribute: attribute)
}
rows.forEach {
[=11=].tagLayout(collectionViewWidth: collectionView?.frame.width ?? 0)
}
return rows.flatMap { [=11=].attributes }
}
}
这是 ViewController 中数据源 cellForRow 处处理 tableview
的代码let cell = tableView.dequeueReusableCell(withIdentifier: "CollectionTableViewCell") as? CollectionTableViewCell
cell?.setupCell(showFirstItem: true, showSecondItem: true)
cell?.collectionView.reloadData()
cell?.layoutIfNeeded()
let height = cell?.collectionView.collectionViewLayout.collectionViewContentSize.height
cell?.collectionViewHeightConstraint.constant = height ?? 0
我已经在访问 collectionview 内容大小高度之前调用了 layoutIfNeeded() 并将内容大小高度分配给 collectionview 高度约束。我已经将 isScrollEnabled 设置为 false。
在 CollectionTableViewCell 中它看起来像这样
@IBOutlet weak var collectionView: UICollectionView!
@IBOutlet weak var collectionViewHeightConstraint: NSLayoutConstraint! // Required priority (1000)
private var collectionItem: [String] = []
override func awakeFromNib() {
super.awakeFromNib()
setupCollectionView()
collectionView.dataSource = self
collectionView.delegate = self
let layout = LeftAlignTagCollectionViewFlowLayout()
layout.estimatedItemSize = CGSize(width: 140, height: 40)
collectionView.collectionViewLayout = layout
}
func setupCell(showFirstItem: Bool, showSecondItem: Bool) {
setupCollectionItem()
}
private func setupCollectionItem(showFirstItem: Bool, showSecondItem: Bool) {
collectionItem = []
if showFirstItem {
let data = "TagNumber1"
collectionItem.append(data)
}
if showSecondItem {
let data = "TagNumber2"
collectionItem.append(data)
}
}
extension CollectionTableViewCell: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
private func setupCollectionView() {
collectionView.register(UINib(nibName: "CollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "CollectionViewCell")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return collectionItem.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as? CollectionViewCell
cell?.configCell(model: collectionItem[indexPath.row], maxWidth: collectionView.frame.width - 8)
cell?.setNeedsLayout()
cell?.layoutIfNeeded()
return cell ?? UICollectionViewCell()
}
}
总而言之,当我 运行 上面的代码时,它会在第一次加载时给出错误的内容大小高度让我们说大约 86.0 但是在第二次滚动或重新加载 tableview 之后(我有两次重新加载 tableview因为在这个 ViewController 中有两种调用 API 的方法,所以它给出了正确的数字,假设我想要的目标是 24.0,所以当从第一个值 86.0 更改为 24.0 时,它会在 uitableviewcell 中出现故障。在完成 collectionview 高度计算过程之前,我无法隐藏 tableview。另一点是,如果我在 collectionItem 中只有一个项目,它会给出正确的 contentsize 高度!但是当它变成两个项目时,问题就出现了。
等待您的建议,谢谢
这本质上是有问题的,有几个原因...
首先,集合视图有几个主要好处:
- 自动滚动
- 通过单元重用进行内存管理
在为“auto-size”集合视图编写代码时,这两个好处都被抛弃了。
其次,当table视图单元首次实例化时,它不知道它的实际宽度是多少 .因此,当集合视图对其单元格进行布局时,它会使用它 开始 的任何宽度——例如,如果它在 Storyboard 中设置为单元格原型,它将使用 Storyboard 中的宽度.
如果您搜索 uicollectionview inside uitableviewcell dynamic height
,您会发现许多讨论和尝试让它发挥作用,并取得了不同程度的成功。
但是,因为 - 如上所述 - 您没有使用 集合视图的两个“最佳功能”,您可能需要采用不同的方法。
看看这个对类似“标签布局”问题的回答。您可能会发现更容易使用: