SwiftUI .swipeActions搭配Core Data时编辑sheet始终传入首条数据如何解决
问题原因
你的实现逻辑存在明确问题:你将sheet(isPresented:)修饰符放在了ForEach循环内部,且所有行共用同一个showingEditTripScreen布尔状态控制显隐。SwiftUI会预渲染列表所有行的视图结构,当你把showingEditTripScreen置为true时,只会触发最先渲染的第一行对应的sheet生效,因此永远传入第一条行程的数据。
可行解决方案
方案一(推荐):使用sheet(item:)API实现
这是苹果官方针对列表选中项弹窗场景设计的API,无需额外维护布尔显隐状态,自动匹配选中项对应的数据。
修改步骤:
- 新增可选类型的选中行程状态变量(需保证你的Trip模型遵守
Identifiable协议,你当前在ForEach中已使用\.id作为唯一标识,默认满足要求):
@State private var selectedTrip: Trip?
- 将原写在ForEach内部的sheet修饰符移除,移动到List/父视图的外层
- 修改编辑按钮的点击逻辑,直接给
selectedTrip赋值为当前点击的行程即可触发弹窗
完整修改后代码示例:
// sheet移到ForEach外层,比如放在List的修饰符位置 List { ForEach(tripVM.trips, id: \.id) { trip in TripCardView(trip: trip) .listRowSeparator(.hidden) .swipeActions(allowsFullSwipe: false) { // Edit Trip Button { selectedTrip = trip // 直接赋值选中的行程 } label: { Label("Edit", systemImage: "pencil.circle.fill") } .tint(.green) // Delete Trip Button { tripVM.deleteTrip(trip: trip) tripVM.getAllTrips() } label: { Label("Delete", systemImage: "trash.circle.fill") } .tint(.red) } } } .sheet(item: $selectedTrip, onDismiss: { selectedTrip = nil }, content: { trip in EditTripScreen(trip: trip) })
方案二:保留isPresented的兼容实现
如果你需要兼容更低版本或者特殊需求要保留原布尔控制逻辑,可以新增可选变量存储选中行程:
- 新增两个状态变量:
@State private var showingEditTripScreen = false @State private var currentEditTrip: Trip?
- 编辑按钮点击时先赋值要编辑的行程,再修改显隐状态:
Button { currentEditTrip = trip showingEditTripScreen = true } label: { Label("Edit", systemImage: "pencil.circle.fill") }
- sheet同样移到ForEach外层,content中使用
currentEditTrip传值:
.sheet(isPresented: $showingEditTripScreen, onDismiss: { currentEditTrip = nil }, content: { if let trip = currentEditTrip { EditTripScreen(trip: trip) } })
注意事项
不要将sheet、alert这类全局弹窗类修饰符放在ForEach循环内部,避免重复创建多份弹窗实例导致的取值、显隐异常。
内容的提问来源于stack exchange,提问作者diver91
相关产品推荐
相关产品推荐

