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 高度!但是当它变成两个项目时,问题就出现了。

等待您的建议,谢谢

这本质上是有问题的,有几个原因...

首先,集合视图有几个主要好处:

  1. 自动滚动
  2. 通过单元重用进行内存管理

在为“auto-size”集合视图编写代码时,这两个好处都被抛弃了。

其次,当table视图单元首次实例化时,它不知道它的实际宽度是多少 .因此,当集合视图对其单元格进行布局时,它会使用它 开始 的任何宽度——例如,如果它在 Storyboard 中设置为单元格原型,它将使用 Storyboard 中的宽度.

如果您搜索 uicollectionview inside uitableviewcell dynamic height,您会发现许多讨论和尝试让它发挥作用,并取得了不同程度的成功。

但是,因为 - 如上所述 - 您没有使用 集合视图的两个“最佳功能”,您可能需要采用不同的方法。

看看这个对类似“标签布局”问题的回答。您可能会发现更容易使用: