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

SwiftUI数组遍历优化:首列固定的水平滚动表格实现问询

SwiftUI 首列固定、其余列水平滚动表格的优化实现

核心优化思路

避免原方案中每列单独遍历数据源的冗余操作,改为仅对数据源进行两次遍历:一次渲染固定首列,一次渲染滚动区域内的所有行内容,大幅减少不必要的数组遍历开销。

完整优化代码

1. 数据结构与数据源定义

struct SampleData: Identifiable {
    let id = UUID()
    let fixedColumn: String
    let column1: String
    let column2: String
    let column3: String
    // 可根据业务需求扩展更多列字段
}

// 测试数据源
let datas: [SampleData] = [
    SampleData(fixedColumn: "行1", column1: "数据1-1", column2: "数据1-2", column3: "数据1-3"),
    SampleData(fixedColumn: "行2", column1: "数据2-1", column2: "数据2-2", column3: "数据2-3"),
    SampleData(fixedColumn: "行3", column1: "数据3-1", column2: "数据3-2", column3: "数据3-3"),
    SampleData(fixedColumn: "行4", column1: "数据4-1", column2: "数据4-2", column3: "数据4-3"),
]

2. 优化后的表格视图

struct TablesView: View {
    let datas: [SampleData]
    
    // 统一管理滚动列的表头标题,便于维护
    private let scrollableColumnTitles = ["列1", "列2", "列3"]
    
    var body: some View {
        HStack(spacing: 0) {
            // 固定首列(包含表头+内容)
            VStack(spacing: 0) {
                // 首列表头
                Text("固定列")
                    .frame(width: 120, height: 50)
                    .background(Color.gray.opacity(0.2))
                    .border(Color.black)
                
                // 首列内容区域
                ScrollView {
                    VStack(spacing: 0) {
                        ForEach(datas) { item in
                            Text(item.fixedColumn)
                                .frame(width: 120, height: 50)
                                .border(Color.black)
                        }
                    }
                }
            }
            
            // 可水平滚动的列区域
            ScrollView(.horizontal) {
                VStack(spacing: 0) {
                    // 滚动列表头
                    HStack(spacing: 0) {
                        ForEach(scrollableColumnTitles, id: \.self) { title in
                            Text(title)
                                .frame(width: 150, height: 50)
                                .background(Color.gray.opacity(0.2))
                                .border(Color.black)
                        }
                    }
                    
                    // 滚动列内容:仅遍历一次数据源,每行渲染所有非首列数据
                    VStack(spacing: 0) {
                        ForEach(datas) { item in
                            HStack(spacing: 0) {
                                Text(item.column1)
                                    .frame(width: 150, height: 50)
                                    .border(Color.black)
                                Text(item.column2)
                                    .frame(width: 150, height: 50)
                                    .border(Color.black)
                                Text(item.column3)
                                    .frame(width: 150, height: 50)
                                    .border(Color.black)
                                // 新增列时,只需在此添加对应字段的Text即可
                            }
                        }
                    }
                }
            }
        }
    }
}

进阶优化:同步垂直滚动位置

如果需要首列与滚动区域的垂直滚动保持同步,可以通过监听滚动偏移量实现:

struct TablesView: View {
    let datas: [SampleData]
    private let scrollableColumnTitles = ["列1", "列2", "列3"]
    
    // 绑定滚动偏移量,用于同步两边滚动
    @State private var scrollOffset: CGFloat = 0
    
    var body: some View {
        HStack(spacing: 0) {
            // 固定首列
            VStack(spacing: 0) {
                Text("固定列")
                    .frame(width: 120, height: 50)
                    .background(Color.gray.opacity(0.2))
                    .border(Color.black)
                
                ScrollView {
                    VStack(spacing: 0) {
                        ForEach(datas) { item in
                            Text(item.fixedColumn)
                                .frame(width: 120, height: 50)
                                .border(Color.black)
                        }
                    }
                    // 监听首列的滚动偏移
                    .background(
                        GeometryReader { proxy in
                            Color.clear
                                .preference(key: ScrollOffsetKey.self, value: proxy.frame(in: .named("scrollSync")).minY)
                        }
                    )
                }
                .coordinateSpace(name: "scrollSync")
                .onPreferenceChange(ScrollOffsetKey.self) { newValue in
                    scrollOffset = newValue
                }
            }
            
            // 可滚动区域
            ScrollView(.horizontal) {
                ScrollView {
                    VStack(spacing: 0) {
                        // 滚动列表头
                        HStack(spacing: 0) {
                            ForEach(scrollableColumnTitles, id: \.self) { title in
                                Text(title)
                                    .frame(width: 150, height: 50)
                                    .background(Color.gray.opacity(0.2))
                                    .border(Color.black)
                            }
                        }
                        
                        // 滚动列内容
                        VStack(spacing: 0) {
                            ForEach(datas) { item in
                                HStack(spacing: 0) {
                                    Text(item.column1)
                                        .frame(width: 150, height: 50)
                                        .border(Color.black)
                                    Text(item.column2)
                                        .frame(width: 150, height: 50)
                                        .border(Color.black)
                                    Text(item.column3)
                                        .frame(width: 150, height: 50)
                                        .border(Color.black)
                                }
                            }
                        }
                    }
                }
                // 同步首列的滚动偏移
                .offset(y: scrollOffset)
                .coordinateSpace(name: "scrollSync")
            }
        }
    }
}

// 自定义PreferenceKey,用于传递滚动偏移量
private struct ScrollOffsetKey: PreferenceKey {
    static var defaultValue: CGFloat = 0
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {}
}

优化效果说明

  • 原方案每新增一列就多一次数组遍历,优化后无论多少列,仅需两次遍历
  • 滚动列的表头与内容统一管理,新增/删除列的操作更直观
  • 同步滚动的进阶方案提升了用户操作的连贯性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 16:20:28