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

如何在NavigationSplitView中为每个会话保留表格选中状态

问题解决:为每个Session独立保留Table选中状态

核心问题分析

  1. 选中状态未与模型双向绑定:SessionView中使用@State存储选中行,该状态属于视图本地,切换Session时不会自动同步当前Session的selectedRecord值,导致沿用之前的选中行。
  2. 值类型修改未触发视图更新:TestSession是结构体(值类型),直接修改数组中元素的selectedRecord属性不会触发Model的@Published通知,视图无法感知模型中选中状态的变化。

解决方案

  1. 将SessionView的选中状态改为@Binding,直接绑定到对应TestSession的selectedRecord,实现视图与模型的双向同步。
  2. 调整ContentView中SessionView的调用方式,传递正确的绑定路径,确保切换Session时加载对应会话的选中状态。
  3. 移除冗余的setSelectedRecord方法,通过绑定自动同步模型与视图的状态。

修改后的完整代码

struct ContentView: View {
    @ObservedObject var model: Model
    @State var selection: TestSession.ID?

    var body: some View {
        if model.sessions.count > 0 {
            NavigationSplitView(sidebar: {
                List(selection: $selection) {
                    ForEach(model.sessions, id: \.id) { session in
                        Text("\(session.name)")
                    }
                }
            }, detail: {
                if let selectedId = selection,
                   let sessionIndex = model.sessions.firstIndex(where: { $0.id == selectedId }) {
                    // 传递绑定到对应Session的selectedRecord
                    SessionView(
                        session: model.sessions[sessionIndex],
                        selection: $model.sessions[sessionIndex].selectedRecord
                    )
                } else {
                    Text("Select a session")
                }
            })
        } else {
            Text("No sessions")
        }
    }
}

struct SessionView: View {
    var session: TestSession
    // 使用@Binding替代@State,绑定到模型中的选中状态
    @Binding var selection: TestRecord.ID?

    var body: some View {
        VStack {
            Text("Session \(session.id) - \(session.name) - \(session.records.count)")
            Text("Selected row: \(selection ?? -1)")
            Table(of: TestRecord.self, selection: $selection) {
                TableColumn("Id") { rec in
                    Text("\(String(rec.id))")
                        .font(.system(.body, design: .monospaced))
                }
                TableColumn("Value") { rec in
                    Text("\(String(rec.value))")
                        .font(.system(.body, design: .monospaced))
                }
            } rows: {
                ForEach(session.records) { rec in
                    TableRow(rec)
                }
            }

            Spacer()
        }
        // 移除冗余的onChange,绑定会自动同步状态
    }
}

class Model: ObservableObject {
    @Published var sessions: [TestSession] = []

    init() {
        let n = Int.random(in: 5..<10)
        for i in 0..<n {
            var s = TestSession(name: "session \(i + 1)")
            let m = Int.random(in: 10..<20)
            for j in 0..<m {
                s.addRecord("\(j + 1)")
            }
            self.sessions.append(s)
        }
    }

    // 保留下标方法,用于其他场景(可选)
    subscript(index: UUID) -> TestSession? {
        sessions.first(where: { $0.id == index })
    }

    // 移除冗余的setSelectedRecord方法,绑定自动处理状态同步
}

struct TestSession: Identifiable {
    let id = UUID()
    let name: String
    var records: [TestRecord] = []
    var selectedRecord: TestRecord.ID? // 明确类型为TestRecord的ID

    init(name: String) {
        self.name = name
    }

    mutating func addRecord(_ value: String) {
        let r = TestRecord(id: records.count, value: "\(records.count)")
        records.append(r)
    }
}

struct TestRecord: Identifiable {
    let id: Int
    let value: String
}

关键修改说明

  • SessionView的selection改为@Binding:直接绑定到模型中对应TestSession的selectedRecord,选中行变化时自动同步到模型,切换Session时自动加载当前会话的选中状态。
  • ContentView中传递绑定路径:通过$model.sessions[sessionIndex].selectedRecord获取绑定,确保指向正确的Session实例。
  • 移除冗余方法:删除setSelectedRecord和onChange,利用绑定自动完成状态同步,简化代码逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 06:37:09