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

Swift4中如何让多分区CollectionView的行水平居中?

解决Swift 4多分区CollectionView行水平居中的问题

我太懂这种挫败感了——试过KTCenterFlowLayout却在多分区场景下掉链子,确实很多第三方布局库对多分区的适配都没做周全。不过咱们完全可以自己撸一个自定义UICollectionViewFlowLayout,完美适配多分区的行居中需求,步骤也不复杂:

第一步:创建自定义居中布局类

新建一个继承自UICollectionViewFlowLayout的类,重写布局属性计算方法,对每一行的item进行居中调整:

class CenteredCollectionViewFlowLayout: UICollectionViewFlowLayout {
    
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        // 获取系统默认计算的布局属性并复制一份,避免修改原数据
        guard let originalAttributes = super.layoutAttributesForElements(in: rect) else { return nil }
        let attributesArray = NSArray(array: originalAttributes, copyItems: true) as! [UICollectionViewLayoutAttributes]
        
        // 按「分区+行」分组,把同一分区同一行的cell归为一组
        var rowGroups = [String: [UICollectionViewLayoutAttributes]]()
        for attributes in attributesArray {
            guard attributes.representedElementCategory == .cell else { continue }
            
            // 用分区号和行的y坐标哈希值作为组的key,确保同一行的cell分到一组
            let section = attributes.indexPath.section
            let rowY = attributes.frame.origin.y
            let groupKey = "\(section)-\(rowY.hashValue)"
            
            rowGroups[groupKey, default: []].append(attributes)
        }
        
        // 遍历每组,调整行内cell的位置实现居中
        for (_, rowAttributes) in rowGroups {
            guard !rowAttributes.isEmpty else { continue }
            
            // 计算当前行所有cell的总宽度(包含cell间的间距)
            let totalCellWidth = rowAttributes.reduce(0.0) { $0 + $1.frame.width }
            let totalSpacing = minimumInteritemSpacing * CGFloat(rowAttributes.count - 1)
            let totalRowContentWidth = totalCellWidth + totalSpacing
            
            // 计算行的左右偏移量,让整行居中
            let collectionViewWidth = collectionView?.bounds.width ?? 0
            let sectionInsets = self.sectionInsetForSection(rowAttributes.first!.indexPath.section)
            let availableWidth = collectionViewWidth - sectionInsets.left - sectionInsets.right
            let horizontalInset = (availableWidth - totalRowContentWidth) / 2
            var currentX = sectionInsets.left + max(horizontalInset, 0) // 避免偏移量为负导致内容溢出
            
            // 逐个调整cell的x坐标
            for attributes in rowAttributes {
                attributes.frame.origin.x = currentX
                currentX += attributes.frame.width + minimumInteritemSpacing
            }
        }
        
        return attributesArray
    }
    
    // 适配不同分区的自定义内边距
    private func sectionInsetForSection(_ section: Int) -> UIEdgeInsets {
        if let delegate = collectionView?.delegate as? UICollectionViewDelegateFlowLayout {
            return delegate.collectionView?(collectionView!, layout: self, insetForSectionAt: section) ?? self.sectionInset
        }
        return self.sectionInset
    }
    
    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
        // 屏幕旋转等bounds变化时,重新计算布局
        return true
    }
}

第二步:在ViewController中使用这个布局

初始化CollectionView时,直接用咱们自定义的布局即可,别忘了设置间距、内边距等参数:

override func viewDidLoad() {
    super.viewDidLoad()
    
    let centeredLayout = CenteredCollectionViewFlowLayout()
    centeredLayout.minimumInteritemSpacing = 10 // cell之间的水平间距
    centeredLayout.minimumLineSpacing = 15 // 行之间的垂直间距
    centeredLayout.sectionInset = UIEdgeInsets(top: 20, left: 16, bottom: 20, right: 16) // 默认分区内边距
    
    let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: centeredLayout)
    collectionView.delegate = self
    collectionView.dataSource = self
    collectionView.register(YourCustomCell.self, forCellWithReuseIdentifier: "cellID")
    collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    view.addSubview(collectionView)
}

额外注意点

  • 如果你的分区有不同的sectionInset,上面的代码已经通过sectionInsetForSection方法适配了,只要你在UICollectionViewDelegateFlowLayout的insetForSectionAt方法里返回对应的值就行。
  • 不管你的cell是固定大小还是动态大小(通过sizeForItemAt代理方法设置),这个布局都能正确计算并居中,因为它是基于每个cell的实际frame宽度来计算的。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:45:47