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

在EditView中删除对象后返回至ItemsView的实现问题

SwiftUI 删除Item后同时关闭Sheet和DetailView问题

问题场景

我在大型应用中实现了一个简化的Item管理功能,包含三个视图:

  • ItemsView:展示Item列表
  • DetailView:展示选中的Item详情,提供编辑入口
  • EditView:编辑Item文本或执行删除操作

目前的问题是:删除Item后调用dismiss()关闭EditView的sheet,回到DetailView时,虽然Item已经从ObservableObject的allItems数组中移除,但DetailView仍然显示已删除的Item。我需要实现点击删除按钮后,直接从EditView回到ItemsView,同时关闭sheet和DetailView。

原代码

struct Item: Identifiable, Hashable {
    var id = UUID().uuidString
    var title: String
}

class Items: ObservableObject {
    @Published var allItems = [
        Item(title: "Apple"),
        Item(title: "Boat"),
        Item(title: "Car"),
        Item(title: "Drink")
    ]
}

struct ItemsView: View {
    @EnvironmentObject var items: Items
    var body: some View {
        NavigationStack {
            List {
                ForEach($items.allItems) { $item in
                    NavigationLink {
                        DetailView(item: $item)
                    } label: {
                        Text(item.title)
                    }
                }
            }
            .navigationTitle("Items")
        }
    }
}

struct DetailView: View {
    @Binding var item: Item
    @Environment(\.dismiss) var dismiss
    @State private var isEditViewShowing = false
    var body: some View {
        VStack {
            Text(item.title)
        }
        .sheet(isPresented: $isEditViewShowing, content: {
            NavigationStack {
                EditView(item: $item)
            }
        })
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                Button("Edit") {
                    isEditViewShowing = true
                }
            }
        }
    }
}

struct EditView: View {
    @EnvironmentObject var items: Items
    @Environment(\.dismiss) var dismiss
    @Binding var item: Item
    var body: some View {
        Form {
            TextField("New", text: $item.title)
            Button("Delete") {
                items.allItems.removeAll {
                    $0.id == item.id
                }
                dismiss()
            }
        }
        .navigationTitle("Edit View")
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                Button("Save") {
                    dismiss()
                }
            }
        }
    }
}

可行解决方案

方案1:用NavigationPath精准控制导航栈(推荐)

通过NavigationPath管理导航状态,删除Item后直接重置导航栈回到列表页:

  1. 修改ItemsView,引入NavigationPath:
struct ItemsView: View {
    @EnvironmentObject var items: Items
    @State private var navigationPath = NavigationPath()
    
    var body: some View {
        NavigationStack(path: $navigationPath) {
            List {
                ForEach(items.allItems) { item in
                    NavigationLink(value: item) {
                        Text(item.title)
                    }
                }
            }
            .navigationTitle("Items")
            .navigationDestination(for: Item.self) { item in
                // 检查Item是否存在,不存在则直接返回列表
                guard let index = items.allItems.firstIndex(where: { $0.id == item.id }) else {
                    navigationPath.removeLast()
                    return EmptyView()
                }
                DetailView(item: $items.allItems[index], navigationPath: $navigationPath)
            }
        }
    }
}
  1. 更新DetailView,接收navigationPath绑定:
struct DetailView: View {
    @Binding var item: Item
    @Binding var navigationPath: NavigationPath
    @State private var isEditViewShowing = false
    
    var body: some View {
        VStack {
            Text(item.title)
        }
        .sheet(isPresented: $isEditViewShowing) {
            NavigationStack {
                EditView(item: $item, navigationPath: $navigationPath)
            }
        }
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                Button("Edit") {
                    isEditViewShowing = true
                }
            }
        }
    }
}
  1. 修改EditView,删除后同时关闭sheet并退回列表:
struct EditView: View {
    @EnvironmentObject var items: Items
    @Environment(\.dismiss) var dismiss
    @Binding var item: Item
    @Binding var navigationPath: NavigationPath
    
    var body: some View {
        Form {
            TextField("New", text: $item.title)
            Button("Delete") {
                items.allItems.removeAll { $0.id == item.id }
                dismiss() // 关闭EditView的sheet
                navigationPath.removeLast() // 从导航栈移除DetailView
            }
        }
        .navigationTitle("Edit View")
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                Button("Save") {
                    dismiss()
                }
            }
        }
    }
}

方案2:在DetailView中监听Item状态自动返回

如果不想改动导航方式,可以在DetailView中监听allItems数组的变化,当Item被删除时自动返回列表:

修改DetailView:

struct DetailView: View {
    @Binding var item: Item
    @EnvironmentObject var items: Items
    @Environment(\.dismiss) var dismiss
    @State private var isEditViewShowing = false
    
    var body: some View {
        VStack {
            Text(item.title)
        }
        .sheet(isPresented: $isEditViewShowing) {
            NavigationStack {
                EditView(item: $item)
            }
        }
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                Button("Edit") {
                    isEditViewShowing = true
                }
            }
        }
        .onChange(of: items.allItems) { newItems in
            // 检查当前Item是否还在数组中
            if !newItems.contains(where: { $0.id == item.id }) {
                dismiss() // 自动返回ItemsView
            }
        }
    }
}

这种方案无需修改导航栈逻辑,仅通过监听数组变化实现自动返回,适合不想大幅改动原有代码的场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 10:09:58