SwiftUI中如何在ForEach循环内传递选中项的指定值
问题解决方案:列表项选中后Sheet传递错误标识符
问题原因
你当前的代码存在两个核心问题:
- 在
ForEach循环内给每个按钮都绑定了同一个showSheet状态和onChange监听,当点击任意按钮触发showSheet变为true时,所有循环中的onChange闭包都会执行一遍,最终theIdentifer会被最后一个循环项的row值覆盖。 - 每个循环项都单独绑定了
sheet,这会导致多个Sheet实例被创建,进一步加剧状态覆盖的问题。
解决方案一:使用sheet(item:)(推荐)
这种方式专门用于绑定选中项的场景,无需额外的布尔状态控制Sheet显示,直接绑定选中的数据源项。
修改后的完整代码
struct Test: View { @StateObject var vm = ViewModel() var body: some View { VStack { ForEach(vm.someData, id: \.self) { row in Button { vm.selectedRow = row // 点击时直接赋值选中项 } label: { Text("\(row)") } } } .padding() // 绑定选中项,非nil时自动展示Sheet .sheet(item: $vm.selectedRow) { selectedRow in TheSheet(selectedRow: selectedRow) } } } extension Test { @MainActor class ViewModel: ObservableObject { @Published var selectedRow: Int? // 存储选中项,可选类型 @Published var someData = [2, 4, 6, 8, 10, 12] } } struct TheSheet: View { let selectedRow: Int @Environment(\.dismiss) private var dismiss // 用环境变量关闭Sheet var body: some View { VStack { Text("选中的标识符:\(selectedRow)") Button { dismiss() // 关闭Sheet,selectedRow会自动置为nil } label: { Text("Close") } } .frame(width: 400, height: 200) } } // 让Int遵循Identifiable协议,满足sheet(item:)的要求 extension Int: Identifiable { public var id: Int { self } }
解决方案二:保留isPresented的修复方案
如果坚持使用布尔状态控制Sheet,需要将sheet移出循环,并在按钮点击时直接完成赋值操作,避免多个onChange触发覆盖。
修改后的核心代码
struct Test: View { @StateObject var vm = ViewModel() var body: some View { VStack { ForEach(vm.someData, id: \.self) { row in Button { vm.theIdentifer = row // 先赋值选中项 vm.showSheet.toggle() // 再打开Sheet } label: { Text("\(row)") } } } .padding() // 将sheet移到循环外,只绑定一次 .sheet(isPresented: $vm.showSheet) { TheSheet(vm: vm) } } } extension Test { @MainActor class ViewModel: ObservableObject { @Published var showSheet = false @Published var theIdentifer = 0 @Published var someData = [2, 4, 6, 8, 10, 12] } } // TheSheet代码保持不变
内容的提问来源于stack exchange,提问作者Steve
相关产品推荐
相关产品推荐

