如何在NavigationSplitView中为每个会话保留表格选中状态
问题解决:为每个Session独立保留Table选中状态
核心问题分析
- 选中状态未与模型双向绑定:
SessionView中使用@State存储选中行,该状态属于视图本地,切换Session时不会自动同步当前Session的selectedRecord值,导致沿用之前的选中行。 - 值类型修改未触发视图更新:
TestSession是结构体(值类型),直接修改数组中元素的selectedRecord属性不会触发Model的@Published通知,视图无法感知模型中选中状态的变化。
解决方案
- 将
SessionView的选中状态改为@Binding,直接绑定到对应TestSession的selectedRecord,实现视图与模型的双向同步。 - 调整
ContentView中SessionView的调用方式,传递正确的绑定路径,确保切换Session时加载对应会话的选中状态。 - 移除冗余的
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
相关产品推荐
相关产品推荐

