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

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的绑定传递过去:

  1. 修改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")
        }
    }
}
  1. 在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标记集合,集合变化时视图会自动刷新:

  1. 创建状态容器类:
import SwiftUI
import CoreData

class ExerciseSelectionState: ObservableObject {
    @Published var selectedItems = Set<Exercise>()
}
  1. 在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)
        }
    }
}
  1. 修改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() }
            }
        }
    }
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 07:40:21