如何实现LazyVGrid内元素自适应自身尺寸的均匀间距布局
问题原因说明
你当前代码出现元素全部垂直堆叠的原因是 columns 参数只传入了1个 GridItem,相当于强制指定布局只有1列,所以所有元素只能纵向排列。LazyVGrid 本身是基于列规则驱动的布局,所有列的宽度会遵循你指定的 GridItem 规则(fixed/flexible/adaptive),无法支持不同宽度的元素自动填充换行,所以没法直接满足你的需求。
实现方案
方案1:iOS 16+ 自定义流式布局(推荐)
基于SwiftUI的 Layout 协议实现流式布局,自动根据元素的实际尺寸排列,满行自动换行,支持自定义横向、纵向间距,不需要提前确定一行可容纳的元素数量。
示例代码如下:
// 自定义自适应流式布局 struct FlowLayout: Layout { // 元素之间的统一间距 let itemSpacing: CGFloat // 行间距 let lineSpacing: CGFloat init(itemSpacing: CGFloat = 8, lineSpacing: CGFloat = 8) { self.itemSpacing = itemSpacing self.lineSpacing = lineSpacing } func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { let itemSizes = subviews.map { $0.sizeThatFits(.unspecified) } var totalHeight: CGFloat = 0 var currentRowWidth: CGFloat = 0 var currentRowMaxHeight: CGFloat = 0 let containerWidth = proposal.width ?? .greatestFiniteMagnitude for size in itemSizes { // 当前行放不下就换行 if currentRowWidth + size.width + itemSpacing > containerWidth { totalHeight += currentRowMaxHeight + lineSpacing currentRowWidth = size.width currentRowMaxHeight = size.height } else { currentRowWidth += size.width + itemSpacing currentRowMaxHeight = max(currentRowMaxHeight, size.height) } } totalHeight += currentRowMaxHeight return CGSize(width: containerWidth, height: totalHeight) } func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { let itemSizes = subviews.map { $0.sizeThatFits(.unspecified) } var currentX = bounds.minX var currentY = bounds.minY var currentRowMaxHeight: CGFloat = 0 for (index, subview) in subviews.enumerated() { let size = itemSizes[index] if currentX + size.width + itemSpacing > bounds.maxX { // 换行 currentY += currentRowMaxHeight + lineSpacing currentX = bounds.minX currentRowMaxHeight = size.height } subview.place(at: CGPoint(x: currentX, y: currentY), proposal: ProposedViewSize(size)) currentX += size.width + itemSpacing currentRowMaxHeight = max(currentRowMaxHeight, size.height) } } }
使用方式:
ScrollView { FlowLayout(itemSpacing: 12, lineSpacing: 12) { Color.blue .frame(width: 200, height: 200) Color.blue .frame(width: 100, height: 100) Color.blue .frame(width: 100, height: 100) // 任意尺寸的元素都可自动排列 } .padding() }
方案2:兼容低iOS版本
如果需要支持iOS 16以下的系统,可以桥接UIKit的 UICollectionView,做如下配置即可实现相同效果:
- 将
UICollectionViewFlowLayout的estimatedItemSize设置为UICollectionViewFlowLayout.automaticSize - 自定义
minimumInteritemSpacing(元素间距)和minimumLineSpacing(行间距)为你需要的数值 - Cell 不需要手动设置固定尺寸,由内部内容自动撑开即可
如果元素数量非常多、对滚动性能要求高,也更推荐使用该方案,因为UICollectionView自带懒加载机制,性能优于一次性布局所有元素的自定义FlowLayout。
内容的提问来源于stack exchange,提问作者Boothosh81
相关产品推荐
相关产品推荐

