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

SwiftUI中如何实现LazyVGrid的区块对齐效果?

SwiftUI实现区块对齐的自定义网格布局

为什么原生LazyVGrid达不到预期?

LazyVGrid是行驱动的网格布局,每行高度由该行最高元素决定,所有元素都会被“拉平”到同一行高,没法实现不同尺寸元素的流式填充(也就是你要的区块对齐/瀑布流效果)。要实现需求,得自己写自定义布局。

解决方案:用Layout协议实现自适应布局(iOS 16+)

我们可以基于SwiftUI的Layout协议,实现一个能自动把元素放到当前最矮列的布局,完美适配大(b)、小(s)元素的动态组合。

1. 先定义元素尺寸规则

先明确大小元素的尺寸,这里假设小元素是80x80,大元素是80x160(你可以根据需求随便改):

enum GridBlockSize {
    case small // s
    case big   // b
    
    var size: CGSize {
        switch self {
        case .small: return CGSize(width: 80, height: 80)
        case .big: return CGSize(width: 80, height: 160)
        }
    }
}

2. 实现自定义瀑布流布局

这个布局会跟踪每一列的当前高度,每次把新元素放到高度最低的列里,自动填充空隙:

struct BlockAlignedGridLayout: Layout {
    let columnCount: Int
    let spacing: CGFloat
    
    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
        guard !subviews.isEmpty else { return .zero }
        
        // 计算每列的基础宽度
        let totalSpacing = CGFloat(columnCount - 1) * spacing
        let columnWidth = (proposal.width ?? 0) - totalSpacing
        let itemWidth = columnWidth / CGFloat(columnCount)
        
        // 记录每列的总高度
        var columnHeights = Array(repeating: 0.0, count: columnCount)
        
        for subview in subviews {
            // 找到当前最矮的列
            let shortestColumnIndex = columnHeights.firstIndex(of: columnHeights.min()!)!
            // 获取元素的实际高度
            let itemSize = subview.sizeThatFits(.init(width: itemWidth, height: nil))
            // 更新列高度
            columnHeights[shortestColumnIndex] += itemSize.height + spacing
        }
        
        return CGSize(
            width: proposal.width ?? 0,
            height: columnHeights.max()! - spacing // 减去最后一个多余的间距
        )
    }
    
    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
        guard !subviews.isEmpty else { return }
        
        let totalSpacing = CGFloat(columnCount - 1) * spacing
        let columnWidth = bounds.width - totalSpacing
        let itemWidth = columnWidth / CGFloat(columnCount)
        
        var columnHeights = Array(repeating: bounds.minY, count: columnCount)
        
        for subview in subviews {
            let shortestColumnIndex = columnHeights.firstIndex(of: columnHeights.min()!)!
            // 计算元素的位置
            let x = bounds.minX + CGFloat(shortestColumnIndex) * (itemWidth + spacing)
            let y = columnHeights[shortestColumnIndex]
            // 获取元素尺寸
            let itemSize = subview.sizeThatFits(.init(width: itemWidth, height: nil))
            
            // 放置元素
            subview.place(
                at: CGPoint(x: x, y: y),
                proposal: .init(width: itemWidth, height: itemSize.height)
            )
            
            // 更新列高度
            columnHeights[shortestColumnIndex] += itemSize.height + spacing
        }
    }
}

3. 用自定义布局构建动态网格

根据你的元素组合(比如[.big, .small, .big])生成视图:

struct DynamicBlockGrid: View {
    // 这里可以替换成你的动态数据源,比如从接口获取的组合
    let blockSequence: [GridBlockSize] = [.big, .small, .big, .small, .small, .big, .small]
    
    var body: some View {
        ScrollView {
            BlockAlignedGridLayout(columnCount: 2, spacing: 16) {
                ForEach(blockSequence.indices, id: \.self) { idx in
                    let block = blockSequence[idx]
                    ZStack {
                        RoundedRectangle(cornerRadius: 8)
                            .fill(Color.accentColor.opacity(0.8))
                        Text(block == .small ? "s" : "b")
                            .font(.title)
                            .foregroundColor(.white)
                    }
                    .frame(width: block.size.width, height: block.size.height)
                }
            }
            .padding()
        }
    }
}

4. 适配大元素占多列的情况

如果你的大元素是占2列(比如宽度是小元素的2倍),可以修改布局逻辑:判断元素类型后,找到连续可用的多列,放置大元素后同时更新这些列的高度,核心逻辑可以基于上面的Layout扩展实现。

备选方案

如果不想自己写布局,也可以用第三方库,比如SwiftUIMasonry,直接集成就能实现类似效果。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 16:50:21