You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用UICollectionViewCompositionalLayout的自适配单元格在pageSheet中的异常修复

Fixing Self-Sizing UICollectionView Cells in iOS 13+ Page Sheets (Compositional Layout)

I've run into this exact issue before with page sheets and self-sizing compositional layout cells, and yes—you absolutely can fix this while keeping both the UICollectionViewCompositionalLayout and page sheet presentation style. The root problem is that when you drag the page sheet to resize it, the collection view's cached layout calculations don't update properly for self-sizing cells. Here's a step-by-step solution:

1. Fix the Cell Layout with Proper Auto Layout

You mentioned trying Auto Layout instead of sizeThatFits(_:), but it's likely your constraints weren't set up to fully drive the cell's size. Let's rewrite the cell to use explicit constraints that tie directly to its content:

final class Cell: UICollectionViewCell {
    static let reuseIdentifier = "Cell"
    
    lazy var label: UILabel = {
        let label = UILabel()
        label.textAlignment = .center
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        contentView.addSubview(label)
        contentView.backgroundColor = .tertiarySystemFill
        
        // Pin the label with padding, and fix the cell height
        NSLayoutConstraint.activate([
            label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
            label.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
            label.heightAnchor.constraint(equalToConstant: 32),
            contentView.heightAnchor.constraint(equalToConstant: 32)
        ])
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

This setup ensures the cell's width is determined entirely by the label's content (plus padding), and the height stays fixed as intended. We can ditch sizeThatFits(_:) entirely now—Auto Layout handles the sizing.

2. Listen for Page Sheet Size Changes to Refresh Layout

When you drag the page sheet, the view controller's bounds change, but the collection view doesn't always invalidate its layout automatically. We'll add an observer to force a layout refresh when this happens:

final class BugViewController: UIViewController {
    // ... (your existing properties and setup) ...
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(collectionView)
        
        // Add observer for bounds changes (triggered when page sheet is resized)
        view.addObserver(self, forKeyPath: "bounds", options: .new, context: nil)
        
        // Apply your snapshot as before
        var snapshot = NSDiffableDataSourceSnapshot<Int, String>()
        snapshot.appendSections([0])
        snapshot.appendItems(models)
        dataSource.apply(snapshot)
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        guard keyPath == "bounds" else {
            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
            return
        }
        
        // Invalidate the layout to recalculate cell sizes, then refresh visible cells
        collectionView.collectionViewLayout.invalidateLayout()
        collectionView.reloadItems(at: collectionView.indexPathsForVisibleItems)
    }
    
    deinit {
        // Clean up the observer to avoid memory leaks
        view.removeObserver(self, forKeyPath: "bounds")
    }
    
    // ... (your existing methods) ...
}

This ensures every time the page sheet's size changes, the collection view discards its cached layout and recalculates all cell sizes from scratch.

3. Optimize the Compositional Layout Configuration

Tweak the layout to be more flexible with estimated sizes and explicit spacing, which helps avoid layout shifts and incorrect offsets:

private func createCollectionViewLayout() -> UICollectionViewCompositionalLayout {
    // Item size: estimated width (smaller initial value for better flexibility) + fixed height
    let itemSize = NSCollectionLayoutSize(
        widthDimension: .estimated(100),
        heightDimension: .absolute(32)
    )
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
    
    // Group size matches item size (since it's a horizontal group)
    let groupSize = NSCollectionLayoutSize(
        widthDimension: .estimated(100),
        heightDimension: .absolute(32)
    )
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
    group.interItemSpacing = .fixed(8) // Explicit spacing between cells
    
    let section = NSCollectionLayoutSection(group: group)
    section.orthogonalScrollingBehavior = .continuous
    section.contentInsets = NSDirectionalEdgeInsets(top: 44, leading: 16, bottom: 0, trailing: 16) // Move top inset to section instead of collection view
    
    return UICollectionViewCompositionalLayout(section: section)
}

Note that we moved the top content inset from the collection view to the section—this keeps the layout logic centralized and avoids conflicts with the collection view's own insets.

Why This Works

  • Auto Layout: Eliminates the ambiguity of sizeThatFits(_:), ensuring the cell's size is always tied directly to its content.
  • Bounds Observer: Forces the collection view to recalculate layout every time the page sheet resizes, preventing cached incorrect sizes.
  • Layout Tweaks: Using a smaller estimated width gives the layout system more room to adjust, and explicit spacing/insets prevent unexpected offsets.

With these changes, your self-sizing cells will behave correctly even when dragging the page sheet—no more missing cells, wrong sizes, or horizontal shifts.

内容的提问来源于stack exchange,提问作者JWK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 15:02:36