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

如何让SwiftUI Grid指定行可滚动并固定表头与总计行

SwiftUI 实现固定表头与总计行、中间内容滚动的Grid布局

要实现表头和总计行固定、中间内容可滚动的效果,不能直接给单个GridRow套ScrollView——因为Grid是整体布局容器,需要把结构拆分为表头Grid、滚动内容Grid、总计Grid三部分,用VStack组合,同时保证三部分的列宽对齐。

基础实现代码

import SwiftUI

struct ContentView: View {
    // 模拟23行数据
    let data: [(name: String, age: String)] = Array(repeating: ("Test User", "30"), count: 23)
    
    var body: some View {
        VStack(spacing: 0) {
            // 固定表头
            Grid {
                GridRow {
                    Text("Name")
                    Text("Age")
                }
                .font(.headline)
            }
            .padding(.horizontal)
            
            // 中间可滚动内容区
            ScrollView {
                Grid {
                    ForEach(data, id: \.name) { item in
                        GridRow {
                            Text(item.name)
                            Text(item.age)
                        }
                    }
                }
                .padding(.horizontal)
            }
            
            // 固定总计行
            Grid {
                GridRow {
                    Text("Total Age:")
                    Text("\(data.reduce(0) { $0 + Int($1.age)! })")
                }
            }
            .font(.headline)
            .padding(.horizontal)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

优化列对齐(避免错位)

直接拆分Grid可能出现表头、内容、总计的列宽不一致的问题,用GeometryReader计算列宽,强制三部分列宽统一:

import SwiftUI

struct ContentView: View {
    let data: [(name: String, age: String)] = Array(repeating: ("Test User", "30"), count: 23)
    // 自定义列宽比例
    private let nameColumnRatio: CGFloat = 2/3
    private let ageColumnRatio: CGFloat = 1/3
    
    var body: some View {
        GeometryReader { geometry in
            let totalAvailableWidth = geometry.size.width - 32 // 减去左右padding总和
            let nameColumnWidth = totalAvailableWidth * nameColumnRatio
            let ageColumnWidth = totalAvailableWidth * ageColumnRatio
            
            VStack(spacing: 0) {
                // 表头行
                Grid {
                    GridRow {
                        Text("Name")
                            .frame(width: nameColumnWidth, alignment: .leading)
                        Text("Age")
                            .frame(width: ageColumnWidth, alignment: .trailing)
                    }
                    .font(.headline)
                }
                .padding(.horizontal, 16)
                
                // 滚动内容区
                ScrollView {
                    Grid {
                        ForEach(data, id: \.name) { item in
                            GridRow {
                                Text(item.name)
                                    .frame(width: nameColumnWidth, alignment: .leading)
                                Text(item.age)
                                    .frame(width: ageColumnWidth, alignment: .trailing)
                            }
                        }
                    }
                    .padding(.horizontal, 16)
                }
                
                // 总计行
                Grid {
                    GridRow {
                        Text("Total Age:")
                            .frame(width: nameColumnWidth, alignment: .leading)
                        Text("\(data.reduce(0) { $0 + Int($1.age)! })")
                            .frame(width: ageColumnWidth, alignment: .trailing)
                    }
                }
                .font(.headline)
                .padding(.horizontal, 16)
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

这样处理后,表头和总计行固定在界面顶部和底部,中间的23行数据可以自由滚动,同时所有列的宽度保持一致,不会出现错位问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 15:13:12