Swift 4.0中如何正确调整UICollectionView可扩展单元格尺寸并居中
解决UICollectionView单元格水平扩展+居中的统一方案
作为Swift新手遇到这种布局联动问题真的很头疼,分开写两个函数确实容易因为布局周期的问题导致计算不一致——毕竟UICollectionView的布局是一个连贯的流程,拆分逻辑很容易拿到旧的布局数据。我给你一个整合了尺寸计算和居中逻辑的自定义布局方案,完美适配「新单元格水平扩展、最多显示6个」的需求:
核心思路
不要拆分尺寸设置和居中逻辑,把所有布局计算放到UICollectionViewFlowLayout的子类里,这样所有操作都在同一个布局周期内完成,从根源上避免计算不一致的问题。
完整代码实现
import UIKit class CenteredExpandingFlowLayout: UICollectionViewFlowLayout { // 配置参数,你可以根据需求调整 private let maxItemsPerRow = 6 private let itemSpacing: CGFloat = 8 // 单元格之间的间距 private let edgeInsets: UIEdgeInsets = .init(top: 16, left: 16, bottom: 16, right: 16) override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { guard let baseAttributes = super.layoutAttributesForElements(in: rect) else { return nil } // 复制一份属性,避免修改系统原数据 let attributes = baseAttributes.map { $0.copy() as! UICollectionViewLayoutAttributes } guard let collectionView = collectionView, let itemCount = collectionView.dataSource?.collectionView(collectionView, numberOfItemsInSection: 0) else { return attributes } let actualItemCount = min(itemCount, maxItemsPerRow) // 1. 计算每个单元格的尺寸 let totalAvailableWidth = collectionView.bounds.width - edgeInsets.left - edgeInsets.right - (itemSpacing * CGFloat(actualItemCount - 1)) let itemWidth = totalAvailableWidth / CGFloat(actualItemCount) // 这里高度可以自定义,比如固定值或者和宽度成比例 let itemHeight = itemWidth // 示例:正方形单元格 // 更新所有单元格的尺寸 attributes.forEach { attr in if attr.representedElementCategory == .cell { attr.size = CGSize(width: itemWidth, height: itemHeight) } } // 2. 调整单元格位置,实现整体居中 var currentXOffset: CGFloat = 0 if actualItemCount > 0 { let totalItemsWidth = (itemWidth * CGFloat(actualItemCount)) + (itemSpacing * CGFloat(actualItemCount - 1)) // 计算居中需要的左侧偏移量 currentXOffset = (collectionView.bounds.width - totalItemsWidth) / 2 } var previousCellAttr: UICollectionViewLayoutAttributes? attributes.forEach { attr in guard attr.representedElementCategory == .cell else { return } if let prevAttr = previousCellAttr { currentXOffset = prevAttr.frame.maxX + itemSpacing } attr.frame.origin.x = currentXOffset previousCellAttr = attr } return attributes } override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { // 屏幕旋转或容器尺寸变化时,自动重新计算布局 return true } }
使用方法
初始化UICollectionView时直接用这个自定义布局就行:
// 初始化布局 let customLayout = CenteredExpandingFlowLayout() // 创建CollectionView let collectionView = UICollectionView(frame: yourFrame, collectionViewLayout: customLayout) // 后续注册cell、设置dataSource等操作不变
为什么你的旧方案会出问题?
当你用两个独立函数分别处理尺寸和居中时,第一个函数设置完尺寸后,UICollectionView可能还没完成布局刷新,第二个函数获取到的单元格尺寸可能还是系统默认值或者旧的布局数据。而把所有逻辑放到自定义布局里,所有计算都在系统布局流程内完成,尺寸和位置的调整是同步的,自然不会出现不一致的情况。
如果需要调整间距、边距或者单元格高度,直接修改自定义布局里的配置参数就行,非常灵活~
内容的提问来源于stack exchange,提问作者AlexK
相关产品推荐
相关产品推荐

