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

如何实现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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 19:36:03