使用CHTCollectionViewWaterfallLayout时,如何计算网络图片的sizeForItemAt?
解决CHTCollectionViewWaterfallLayout结合SwiftUI卡片的高度计算问题
方案一:继续使用SwiftUI卡片,预计算文本区域高度
SwiftUI的View尺寸可以通过UIHostingController的sizeThatFits方法预计算,无需替换为UIKit,具体步骤如下:
- 抽离文本区域为独立SwiftUI View
把卡片的文本部分单独封装成View,固定宽度为瀑布流的列宽,确保文本自适应布局:
struct CardTextContainer: View { let content: String let fixedWidth: CGFloat var body: some View { Text(content) .font(.system(size: 14)) .lineLimit(nil) .padding(.horizontal, 12) .padding(.vertical, 8) .frame(width: fixedWidth) } }
- 编写文本高度计算函数
用UIHostingController包裹上述View,调用sizeThatFits获取文本区域的实际高度:
func getTextHeight(for content: String, columnWidth: CGFloat) -> CGFloat { let hostVC = UIHostingController(rootView: CardTextContainer(content: content, fixedWidth: columnWidth)) let fittingSize = hostVC.sizeThatFits(CGSize(width: columnWidth, height: CGFloat.greatestFiniteMagnitude)) return fittingSize.height }
- 在
sizeForItemAt中计算总高度
结合图片的缩放高度和文本高度,得到cell的完整尺寸,同时建议缓存计算结果避免重复计算:
// 缓存文本高度的字典,key为indexPath.item private var textHeightCache: [Int: CGFloat] = [:] func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { guard let waterfallLayout = collectionViewLayout as? CHTCollectionViewWaterfallLayout else { return CGSize.zero } // 计算单列宽度:减去内边距和间距 let columnCount = waterfallLayout.columnCount let totalSpacing = waterfallLayout.minimumInteritemSpacing * CGFloat(columnCount - 1) let totalInset = waterfallLayout.sectionInset.left + waterfallLayout.sectionInset.right let columnWidth = (collectionView.bounds.width - totalSpacing - totalInset) / CGFloat(columnCount) let item = yourDataSource[indexPath.item] // 计算图片的显示高度:按列宽等比例缩放 let imageRatio = item.imageWidth / item.imageHeight let imageDisplayHeight = columnWidth / imageRatio // 获取文本高度(优先用缓存) var textHeight: CGFloat if let cachedHeight = textHeightCache[indexPath.item] { textHeight = cachedHeight } else { textHeight = getTextHeight(for: item.textContent, columnWidth: columnWidth) textHeightCache[indexPath.item] = textHeight } // 总高度 = 图片高度 + 文本区域高度 let totalHeight = imageDisplayHeight + textHeight return CGSize(width: columnWidth, height: totalHeight) }
方案二:改用UIKit实现卡片(可选)
如果觉得SwiftUI预计算的方式有性能顾虑,可以改用UIKit的UICollectionViewCell,通过AutoLayout约束让文本区域自适应,然后用systemLayoutSizeFitting计算高度:
构建带AutoLayout的UIKit Cell
在Cell中添加UIImageView和UILabel,设置约束:图片宽度等于Cell宽度,文本在图片下方、宽度等于Cell宽度,且UILabel的行数设为0。计算Cell高度
在sizeForItemAt中,给Cell的图片和文本赋值,然后调用systemLayoutSizeFitting获取完整高度:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { guard let waterfallLayout = collectionViewLayout as? CHTCollectionViewWaterfallLayout else { return CGSize.zero } let columnWidth = (collectionView.bounds.width - waterfallLayout.minimumInteritemSpacing * CGFloat(waterfallLayout.columnCount - 1) - waterfallLayout.sectionInset.left - waterfallLayout.sectionInset.right) / CGFloat(waterfallLayout.columnCount) let item = yourDataSource[indexPath.item] let cell = YourUICollectionViewCell() // 配置图片和文本 cell.imageView.contentMode = .scaleAspectFit cell.imageView.widthAnchor.constraint(equalToConstant: columnWidth).isActive = true cell.textLabel.text = item.textContent // 计算Cell高度 let fittingSize = cell.systemLayoutSizeFitting(CGSize(width: columnWidth, height: CGFloat.greatestFiniteMagnitude), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel) return CGSize(width: columnWidth, height: fittingSize.height) }
注意事项
- 缓存文本/Cell高度能大幅提升列表滚动性能,避免重复计算;如果内容更新,记得清空对应缓存并刷新Cell。
- 列宽计算要严格考虑瀑布流的内边距、间距和列数,确保和实际布局一致。
内容的提问来源于stack exchange,提问作者pang
相关产品推荐
相关产品推荐

