SwiftUI中用Sheet删列表项:首次点最后项Sheet冻结问题求解
解决方案:修复Sheet冻结问题并兼容sheet/fullScreenCover
核心问题分析
首次点击列表最后一项时Sheet冻结,本质是列表项的身份标识不稳定,加上弹窗状态直接绑定KMM对象导致SwiftUI的视图协调逻辑冲突:KMM的Item作为外部值类型,每次数据源更新会生成新对象,而列表最后一项的布局刷新时机刚好和Sheet的初始化逻辑重叠,引发界面冻结。
关键修复步骤
给KMM Item绑定稳定唯一标识
由于无法让KMM Item实现SwiftUI的Identifiable,直接使用Item自身的业务唯一字段(比如id)作为ForEach的key,确保列表项的身份在数据源更新时保持稳定。用ID而非对象管理弹窗状态
避免直接将KMM Item对象绑定为弹窗状态,改用Item的唯一ID作为状态变量。这样即使数据源更新生成新的Item对象,弹窗状态也不会因为对象引用变化而异常。统一兼容sheet和fullScreenCover
扩展ID类型(比如String)遵循Identifiable,让弹窗的状态绑定逻辑同时适配sheet(item:)和fullScreenCover(item:)。
完整代码示例
假设KMM端提供的Item结构如下:
// KMM 定义的数据源模型 data class KMMItem(val id: String, val name: String, val detail: String)
SwiftUI端实现:
// 扩展String遵循Identifiable,适配弹窗的item绑定 extension String: Identifiable { public var id: String { self } } struct ItemListView: View { // 从KMM获取的数据源,用@State/@ObservedObject包装以响应更新 @State private var kmmItems: [KMMItem] = [] // 用Item的唯一ID管理弹窗状态,而非直接存储Item对象 @State private var selectedItemId: String? var body: some View { List { // 用KMM Item自身的稳定id作为ForEach的key ForEach(kmmItems, id: \.id) { item in Text(item.name) .onTapGesture { selectedItemId = item.id } } } // 兼容sheet弹窗(替换成fullScreenCover逻辑完全一致) .sheet(item: $selectedItemId) { itemId in if let targetItem = kmmItems.first(where: { $0.id == itemId }) { ItemDetailPopup(item: targetItem, onDelete: { // 删除逻辑:移除对应Item并关闭弹窗 kmmItems.removeAll(where: { $0.id == itemId }) selectedItemId = nil }, onDismiss: { selectedItemId = nil }) } else { // 处理Item已被删除的边界情况,自动关闭弹窗 Color.clear.onAppear { selectedItemId = nil } } } } } // 弹窗详情视图 struct ItemDetailPopup: View { let item: KMMItem let onDelete: () -> Void let onDismiss: () -> Void var body: some View { VStack(spacing: 20) { Text("详情:\(item.detail)") .font(.title) .padding() Button("删除该项") { onDelete() } .foregroundColor(.red) .buttonStyle(.borderedProminent) Spacer() } .padding() .toolbar { Button("关闭") { onDismiss() } } } }
额外说明
- 如果KMM Item没有自带业务唯一ID,优先让KMM端补充;临时方案可以用Item的核心属性拼接生成稳定key(比如
"\(item.name)-\(item.detail)"),但不建议用hashValue(存在值相同但hash不同的风险)。 - 弹窗中必须处理
targetItem为空的情况:当用户在弹窗显示期间,数据源中的Item被其他逻辑删除时,要自动关闭弹窗避免空值异常。
内容的提问来源于stack exchange,提问作者kojot
相关产品推荐
相关产品推荐

