滚动UICollectionView时可变高度Cell尺寸计算异常求助
UICollectionView可变高度Cell滚动时内容与Cell高度不匹配的解决方法
各位下午好,我有一个包含可变高度Cell的UICollectionView,滚动时内容高度与Cell高度不匹配,求解决方法。
相关代码
1. CollectionView创建代码
// MARK: - UI Fabric. private extension CollectionViewController { func createCollectionView() -> UICollectionView { let layout = UICollectionViewFlowLayout() layout.minimumInteritemSpacing = 16 layout.minimumLineSpacing = 16 layout.scrollDirection = .vertical let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) collectionView.showsVerticalScrollIndicator = false collectionView.translatesAutoresizingMaskIntoConstraints = false return collectionView } }
2. Cell尺寸代理代码
// MARK: - CollectionView Flow Layout. extension CollectionViewController: UICollectionViewDelegateFlowLayout { /// Setting cell sizes. func collectionView( _ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath ) -> CGSize { delegateSettingCell.calculateCellSize(screenWidth: view.bounds.width, index: indexPath.row) } }
3. Cell尺寸计算代码
func calculateCellSize(screenWidth: CGFloat, index: Int) -> CGSize { let item = modelForDisplay[index].addonDetail.count let heightCell = CGFloat((item * 44) + 44) let sizeCellWidth = (view.bounds.width - 32) return CGSize(width: sizeCellWidth, height: heightCell) }
4. Cell实现代码
final class AddonCreatorCell: UICollectionViewCell { // MARK: - Dependencies var delegate: IHandlerAddonCreatorCellDelegate? // MARK: - Public properties static var reuseIdentifier: String = "AddonCreatorCell.cell" // MARK: - Private properties private lazy var textFields: [UITextField] = [] private lazy var viewDie = createView() private lazy var gradientViewDie = createGradient(GradientColors.yellowGradient) private lazy var labelTitle = createUILabel() private lazy var switchShow = createSwitch() private lazy var vStack = createStack() private lazy var headerStack = createStack() // MARK: - Initializator override init(frame: CGRect) { super.init(frame: frame) addUIView() setupConfiguration() setupLayout() } convenience init(handlerAddonCreatorCellDelegate: IHandlerAddonCreatorCellDelegate?) { self.init(frame: CGRect.zero) self.delegate = handlerAddonCreatorCellDelegate } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: - Public methods func reloadFrame(size: CGSize) { self.gradientViewDie.frame.size = size } func reloadData(model: CreateAddon) { // Here I delete unnecessary UIStask with content vStack.subviews.forEach { if $0 != headerStack { $0.removeFromSuperview() } } labelTitle.text = "\(model.title) \(model.addonDetail.count)" model.isActive ? activeSwitch() : deactivateSwitch() switchShow.setOn(model.isActive, animated: false) for (index, element) in model.addonDetail.enumerated() { createContent(item: element, index: index) } } private func createContent(item: AddonDetail, index: Int) { let convertToString = String(item.count) let label = createUILabel() let textField = createTextField(id: index, text: convertToString) let hStack = createStack() label.text = item.title textFields.append(textField) textField.delegate = self textField.addTarget(self, action: #selector(self.myTextFieldChanged(_:)), for: .editingChanged) hStack.axis = .horizontal hStack.alignment = .center hStack.distribution = .fillProportionally hStack.addArrangedSubview(label) hStack.addArrangedSubview(textField) vStack.addArrangedSubview(hStack) addConstraintTextField(textField) } }
补充说明
我做了点击Cell显示尺寸的功能,滚动后尺寸并未改变。
解决方法
1. 替换Frame布局为AutoLayout
你的reloadFrame方法直接修改gradientViewDie的frame,会和AutoLayout冲突。删除该方法,给gradientViewDie添加约束绑定到Cell的contentView:
// 在setupLayout方法中添加以下代码 gradientViewDie.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ gradientViewDie.topAnchor.constraint(equalTo: contentView.topAnchor), gradientViewDie.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), gradientViewDie.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), gradientViewDie.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) ])
确保所有子视图都添加到contentView上,而非直接添加到Cell本身。
2. 确保内容更新后布局生效
在Cell的reloadData方法末尾添加layoutIfNeeded(),确保动态添加的子视图布局计算完成:
func reloadData(model: CreateAddon) { // 原有更新代码... // 添加这行代码 layoutIfNeeded() }
3. 通知CollectionView重新计算尺寸
当数据源变化导致Cell高度改变时,调用以下方法让CollectionView重新计算布局:
// 更新数据源后调用 collectionView.performBatchUpdates(nil, completion: nil) // 或者针对单个Cell更新 collectionView.reloadItems(at: [indexPath])
4. 优化高度计算逻辑
固定数值计算高度容易出现偏差,改用真实Cell预计算高度:
func calculateCellSize(screenWidth: CGFloat, index: Int) -> CGSize { let model = modelForDisplay[index] let cell = AddonCreatorCell() cell.reloadData(model: model) cell.bounds = CGRect(x: 0, y: 0, width: screenWidth - 32, height: .greatestFiniteMagnitude) cell.layoutIfNeeded() let realHeight = cell.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height return CGSize(width: screenWidth - 32, height: realHeight) }
注意:如果Cell数量较多,建议缓存计算后的高度避免重复计算影响性能。
5. 设置FlowLayout预估尺寸
给FlowLayout设置estimatedItemSize,帮助布局系统更准确预估Cell高度,减少滚动跳动:
let layout = UICollectionViewFlowLayout() // 使用自动预估尺寸 layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize // 或者设置一个接近实际的预估高度 // layout.estimatedItemSize = CGSize(width: screenWidth - 32, height: 100)
内容的提问来源于stack exchange,提问作者Steven Kirke
相关产品推荐
相关产品推荐

