SwiftUI技术问题:如何在另一个视图中访问列表选中项
问题描述
我希望在另一个视图中访问列表的选中项。当前代码里selectedItems用@State修饰,无法在其他视图共享;改成@Binding时出现错误:"No exact matches in call to initializer";改成@ObservedObject时又出现错误:"Generic struct 'ObservedObject' requires that 'Set' conform to 'ObservableObject'"。不知道该怎么处理类类型的集合,只希望能在另一个视图中使用selectedItems集合。
初始代码(ExerciseSelectorView)
import SwiftUI struct ExerciseSelectorView: View { @Environment(\.managedObjectContext) var viewContext @Environment(\.dismiss) var dismiss @FetchRequest(sortDescriptors: []) var exercises: FetchedResults<Exercise> @State var selectedItems = Set<Exercise>() var body: some View { NavigationView { VStack { Button("Add") { createExerciseSet() } List(selection: $selectedItems) { ForEach(exercises, id: \.self) { e in Text(e.exercisename) } } .environment(\.editMode, .constant(EditMode.active)) .navigationBarTitle(Text("Selected \(selectedItems.count) rows")) .toolbar { EditButton() } } } } }
目标视图(ExSetView,无法访问selectedItems)
import SwiftUI struct ExSetView: View { @Environment(\.managedObjectContext) var viewContext @Environment(\.dismiss) var dismiss @FetchRequest(sortDescriptors: []) var exsets: FetchedResults<ExerciseSet> var body: some View { NavigationView { VStack (alignment: .leading) { ForEach(selectedItems) { e in NavigationLink( destination: ExSetInputView(selectedItems: e), label: { Text(e.exercise.exercisename) } ) } } } } }
更新后的ExSetView代码
import SwiftUI struct ExSetView: View { @Environment(\.managedObjectContext) var viewContext @Environment(\.dismiss) var dismiss @FetchRequest(sortDescriptors: []) var exsets: FetchedResults<ExerciseSet> @State var selectedItems = Set<Exercise>() var body: some View { NavigationView { VStack (alignment: .leading) { Text("Set Count: \(selectedItems.count)") } } } }
解决方案
方案一:通过@Binding传递选中集合
@Binding需要从父视图传递值,不能在子视图中直接初始化。你需要在ExerciseSelectorView跳转至ExSetView时,把selectedItems的绑定传递过去:
- 修改
ExSetView,将@State改为@Binding:
struct ExSetView: View { @Environment(\.managedObjectContext) var viewContext @Environment(\.dismiss) var dismiss @FetchRequest(sortDescriptors: []) var exsets: FetchedResults<ExerciseSet> // 改为@Binding接收父视图传递的选中集合 @Binding var selectedItems: Set<Exercise> var body: some View { NavigationView { VStack (alignment: .leading) { Text("Set Count: \(selectedItems.count)") // Set无法直接遍历,转换为Array后再使用 ForEach(Array(selectedItems), id: \.self) { e in NavigationLink( destination: ExSetInputView(selectedExercise: e), label: { Text(e.exercisename) } ) } } .navigationBarTitle("Selected Exercises") } } }
- 在
ExerciseSelectorView中添加跳转逻辑(以sheet弹出为例):
struct ExerciseSelectorView: View { @Environment(\.managedObjectContext) var viewContext @Environment(\.dismiss) var dismiss @FetchRequest(sortDescriptors: []) var exercises: FetchedResults<Exercise> @State var selectedItems = Set<Exercise>() // 控制sheet显示的状态 @State private var showExSetView = false var body: some View { NavigationView { VStack { Button("Add") { showExSetView = true } List(selection: $selectedItems) { ForEach(exercises, id: \.self) { e in Text(e.exercisename) } } .environment(\.editMode, .constant(EditMode.active)) .navigationBarTitle(Text("Selected \(selectedItems.count) rows")) .toolbar { EditButton() } } .sheet(isPresented: $showExSetView) { ExSetView(selectedItems: $selectedItems) .environment(\.managedObjectContext, viewContext) } } } }
方案二:使用ObservableObject实现多视图共享状态
如果需要在多个无关视图间共享选中状态,推荐把selectedItems包装到一个遵循ObservableObject的类中,用@Published标记集合,集合变化时视图会自动刷新:
- 创建状态容器类:
import SwiftUI import CoreData class ExerciseSelectionState: ObservableObject { @Published var selectedItems = Set<Exercise>() }
- 在App入口注入状态:
@main struct YourApp: App { let persistenceController = PersistenceController.shared let selectionState = ExerciseSelectionState() var body: some Scene { WindowGroup { ExerciseSelectorView() .environment(\.managedObjectContext, persistenceController.container.viewContext) // 将状态注入环境 .environmentObject(selectionState) } } }
- 修改
ExerciseSelectorView获取共享状态:
struct ExerciseSelectorView: View { @Environment(\.managedObjectContext) var viewContext @Environment(\.dismiss) var dismiss @FetchRequest(sortDescriptors: []) var exercises: FetchedResults<Exercise> // 从环境中获取共享状态 @EnvironmentObject var selectionState: ExerciseSelectionState var body: some View { NavigationView { VStack { Button("Add") { createExerciseSet() dismiss() } List(selection: $selectionState.selectedItems) { ForEach(exercises, id: \.self) { e in Text(e.exercisename) } } .environment(\.editMode, .constant(EditMode.active)) .navigationBarTitle(Text("Selected \(selectionState.selectedItems.count) rows")) .toolbar { EditButton() } } } } }
- 修改
ExSetView获取共享状态:
struct ExSetView: View { @Environment(\.managedObjectContext) var viewContext @Environment(\.dismiss) var dismiss @FetchRequest(sortDescriptors: []) var exsets: FetchedResults<ExerciseSet> // 从环境中获取共享状态 @EnvironmentObject var selectionState: ExerciseSelectionState var body: some View { NavigationView { VStack (alignment: .leading) { Text("Set Count: \(selectionState.selectedItems.count)") ForEach(Array(selectionState.selectedItems), id: \.self) { e in NavigationLink( destination: ExSetInputView(selectedExercise: e), label: { Text(e.exercisename) } ) } } .navigationBarTitle("Selected Exercises") } } }
注意事项
@Binding适合父子视图间的状态传递,结构简单;EnvironmentObject适合多视图跨层级共享状态,扩展性更强;Set<Exercise>无法直接用ForEach遍历,需要转换为Array;- 确保
Exercise类遵循Hashable(Core Data自动生成的类默认已实现,自定义属性时需确认),否则无法存入Set。
内容的提问来源于stack exchange,提问作者Rachel
相关产品推荐
相关产品推荐

