防止 sizeForItemAt 由于重用动态 collectionView 单元格而使用错误的单元格大小

Prevent sizeForItemAt from using the wrong cell size because of reuse for dynamic collectionView cell

我使用自动布局来动态计算 collectionView 单元格的大小。一些单元格在第一次滚动到查看端口时使用来自重用单元格的尺寸。当我继续滚动 collectionView 时,它们将被设置为正确的值。

在我的 sizeForItemAt 中,我有以下内容:

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        if let cachedSize = cachedHeightForIndexPath[indexPath] {
            return cachedSize
        }

        if let cell = collectionView.cellForItem(at: indexPath) {
            cell.setNeedsLayout()
            cell.layoutIfNeeded()
            let size = cell.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
            cachedHeightForIndexPath[indexPath] = size
            print("value is \(size) for indexpath: \(indexPath)")
            return size
        }
        return CGSize(width: ScreenSize.width, height: 0.0)
    }

我有三个会话,第一部分所有单元格的高度理论上都等于 88,所有其他部分所有单元格的高度都等于 104。

最初,只有第一部分是可见的。从控制台中,我可以看到单元格的高度按预期设置为 88.0。当我滚动到其余部分(第一部分将不可见,单元格将被重复使用)时,第二部分和第三部分的一些单元格在第一次滚动到查看端口时使用值 88.0 作为单元格的高度,而不是 104 . 当我继续滚动时,错误大小的单元格将使用 104 作为维度。我们如何强制所有单元格重新计算高度并且不使用旧单元格的高度。

您的想法是正确的,但是当您通过调用 systemLayoutSizeFitting 来测量单元格的内部约束时,而不是在 现有的 上调用 systemLayoutSizeFitting cell (collectionView.cellForItem),你需要用一个 model cell 武装自己,你配置的 cellForItem 会配置它并测量 .

我是这样做的(与您所拥有的非常相似,只有一点不同;另外,我将尺寸存储在模型中):

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let memosize = self.sections[indexPath.section].itemData[indexPath.row].size
    if memosize != .zero {
        return memosize
    }
    self.configure(self.modelCell, forIndexPath:indexPath) // in common with cellForItem
    var sz = self.modelCell.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
    sz.width = ceil(sz.width); sz.height = ceil(sz.height)
    self.sections[indexPath.section].itemData[indexPath.row].size = sz // memoize
    return sz
}