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

SwiftUI删除列表项时确认对话框偶现弹出后消失问题求助

SwiftUI 删除列表单元格时确认对话框不稳定问题解决

问题根源

你遇到的对话框弹出后立即消失的问题,核心原因是确认对话框的附加位置错误:当前你把.confirmationDialog挂在List上,而删除操作会修改project.mirrors数组,触发List(包括内部的DisclosureGroup、ForEach)重新渲染。视图重建过程中,绑定的showingDeleteMirrorAlert状态可能被意外重置,或者对话框所在的视图层级被销毁,导致对话框直接消失。

解决步骤

1. 调整对话框的附加位置

将.confirmationDialog从List的修饰器移到整个视图的根层级(比如Form或者直接在body的最外层视图上),避免列表渲染影响对话框的生命周期。

2. 优化状态与对话框触发方式(可选但更可靠)

改用.confirmationDialog(item:)的形式,直接绑定要删除的Mirror对象,替代布尔值状态和IndexSet存储。这种方式依赖对象的存在性触发对话框,比isPresented更稳定,还能避免索引失效的问题。

修改后的完整代码

struct ProjectDetailView: View {
    let project: Project
    
    @Environment(\.modelContext) private var context
    @Environment(\.dismiss) private var dismiss
    
    // 改用可选对象存储要删除的镜像,替代IndexSet和布尔状态
    @State private var mirrorToDelete: Mirror?
    
    var body: some View {
        Form {
            List {
                DisclosureGroup("Mirrors") {
                    if project.mirrors.isEmpty {
                        ContentUnavailableView {
                            Image(systemName: "figure.strengthtraining.functional")
                            Text("This project has no mirrors.")
                        } description: {
                            Text("Add some if you want some.")
                        }
                    } else {
                        ForEach(project.mirrors) { mirror in
                            NavigationLink(destination: MirrorDetailView(mirrors: mirror)) {
                                MirrorCellView(mirror: mirror)
                            }
                        }
                        .onDelete { indexSet in
                            // 直接获取要删除的对象
                            if let index = indexSet.first {
                                mirrorToDelete = project.mirrors[index]
                            }
                        }
                    }
                }
            }
        }
        // 将对话框移到Form外层,脱离List的渲染影响
        .confirmationDialog(
            "Are you sure you want to delete this mirror?",
            item: $mirrorToDelete,
            titleVisibility: .visible
        ) { mirror in
            Button("Delete", role: .destructive) {
                deleteMirror(mirror: mirror)
                mirrorToDelete = nil
            }
            Button("Cancel", role: .cancel) {
                mirrorToDelete = nil
            }
        } message: { _ in
            Text("This action cannot be undone.")
        }
    }
    
    // 直接接收Mirror对象,无需处理IndexSet
    private func deleteMirror(mirror: Mirror) {
        context.delete(mirror)
        do {
            try context.save()
        } catch {
            print("Error deleting mirror: \(error)")
        }
    }
}

额外说明

  • 原代码中用IndexSet存储待删除项存在隐患:如果在对话框弹出期间列表数据发生变化(比如其他操作修改了数组),索引会失效,导致删除错误。直接存储对象则避免了这个问题。
  • 对话框绑定到根视图后,即使列表重新渲染,对话框的生命周期也不会受影响,能稳定显示直到用户操作完成。

内容的提问来源于stack exchange,提问作者rhythmsticks

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 02:22:47