如何在自定义UICollectionView布局下实现数据3行水平排列展示
实现UICollectionView 3行水平排列居中方案
你当前使用的UICollectionViewCenterLayout是基于垂直流布局的扩展,默认是垂直滚动按行排列元素。要实现6个元素3行水平排列、支持横向滚动且保留居中效果,按以下步骤调整即可:
第一步:基础配置调整
在初始化布局的位置先修改基础属性:
- 把布局的滚动方向改为水平
- 固定每行放2个元素(6个元素刚好占满3行),统一设置cell尺寸、间距参数
- 按需开启分页效果,横向滚动时如果需要整页翻页可以开启
isPagingEnabled
// 布局初始化配置示例 let layout = UICollectionViewCenterHorizontalLayout() layout.scrollDirection = .horizontal // 按实际设计需求调整cell尺寸 layout.itemSize = CGSize(width: 160, height: 80) layout.minimumLineSpacing = 12 // 列与列之间的水平间距 layout.minimumInteritemSpacing = 15 // 同一列上下元素的垂直间距 layout.sectionInset = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20) collectionView.collectionViewLayout = layout // 不需要整页滚动可删除下面一行 collectionView.isPagingEnabled = true
第二步:修改自定义布局适配水平滚动
把原来适配垂直布局的逻辑调整为适配水平滚动的逻辑,完整代码如下:
class CollectionViewColumn { var attributes = [UICollectionViewLayoutAttributes]() var spacing: CGFloat = 0 init(spacing: CGFloat) { self.spacing = spacing } func add(attribute: UICollectionViewLayoutAttributes) { attributes.append(attribute) } // 计算单列总高度 var columnHeight: CGFloat { return attributes.reduce(0, { result, attribute -> CGFloat in return result + attribute.frame.height }) + CGFloat(attributes.count - 1) * spacing } // 单列元素垂直居中 func centerLayout(collectionViewHeight: CGFloat) { let padding = (collectionViewHeight - columnHeight) / 2 var offset = padding for attribute in attributes { attribute.frame.origin.y = offset offset += attribute.frame.height + spacing } } } class UICollectionViewCenterHorizontalLayout: UICollectionViewFlowLayout { // 固定每行元素数量,2个元素一行刚好6个元素占3行 private let itemCountPerRow = 2 override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { guard let attributes = super.layoutAttributesForElements(in: rect) else { return nil } var columns = [CollectionViewColumn]() var currentColumnX: CGFloat = -1 for attribute in attributes { // 水平滚动时X轴中点相同的元素属于同一列 if currentColumnX != attribute.frame.midX { currentColumnX = attribute.frame.midX columns.append(CollectionViewColumn(spacing: minimumInteritemSpacing)) } columns.last?.add(attribute: attribute) } // 每列元素做垂直居中调整 columns.forEach { $0.centerLayout(collectionViewHeight: collectionView?.frame.height ?? 0) } return columns.flatMap { $0.attributes } } // 重写内容尺寸保证滚动范围正确 override var collectionViewContentSize: CGSize { guard let collectionView = collectionView else { return .zero } let totalItemCount = collectionView.numberOfItems(inSection: 0) // 计算总列数:总元素数/每行元素数,向上取整 let totalColumn = Int(ceil(Double(totalItemCount) / Double(itemCountPerRow))) let contentWidth = CGFloat(totalColumn) * (itemSize.width + minimumLineSpacing) + sectionInset.left + sectionInset.right - minimumLineSpacing let contentHeight = collectionView.bounds.height return CGSize(width: contentWidth, height: contentHeight) } }
注意事项
- 如果cell尺寸不固定,需要在
collectionView(_:layout:sizeForItemAt:)代理方法中返回对应cell的实际尺寸 - 后续元素数量变动时,只要调整
itemCountPerRow的数值即可适配不同行数要求 - 代码中间距参数可根据设计需求自行调整,和原有居中逻辑保持一致,仅适配了水平滚动场景
内容的提问来源于stack exchange,提问作者Vijay Kumar
相关产品推荐
相关产品推荐

