当环境对象更新导致同一视图重新加载时,如何导航到另一个 SwiftUI 视图

问题描述

我正在努力实现的目标:我是一名新的 SwiftUI 开发人员。我正在尝试构建一个简单的地址簿应用程序。我有三个观点:

  1. ContentView - 包含列表视图中所有联系人的主视图,导航视图顶部有一个添加联系人 ('+') 和编辑按钮
  2. AddContact 视图 - 具有“姓名”和“电子邮件”文本字段以及“提交”按钮
  3. displayContactDetails 视图 - 与此问题无关。

我创建了一个环境对象“myContacts”,它是一个“联系人”对象数组,并将其传递到 ContentView 中以跟踪地址簿中的所有联系人

用户导航到 AddContact 视图、添加姓名和电子邮件并提交时,我希望更新环境对象“myContacts”,并让用户导航回 ContentView,以便他们可以看到地址簿新联系人包括在内。

问题

用户在 AddContact 视图上按下“提交”时,它会正确调用我创建的导航链接以将用户发送回 ContentView。但是因为环境对象“myContacts”也被提交更新,它立即再次从 ContentView 导航回 AddContact View。因此,它似乎首先执行导航链接,然后由于 myContacts 的刷新而重新加载 AddContact 视图。

代码 - 内容视图

    struct ContentView: View {
    
    @EnvironmentObject var myContacts: Contacts
    @State var isAddButtonpressed: Bool = false
    
    var body: some View {
        
        NavigationView{
           
            List {
                
                ForEach(myContacts.contacts) { item in
                    
                    NavigationLink(
                          //display items and send user to displayContactDetails

                        })
                    
                }
            
            }
            .navigationBarTitle("Address Book")
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading){
                    
                    Button(action: {
                        isAddButtonpressed.toggle()
                    },label: {
                        
                       NavigationLink(
                        destination: AddContactView(),isActive: $isAddButtonpressed,label: {
                            Image(systemName: "plus")
                        })
                        
                    })
                }
                ToolbarItem(placement: .navigationBarTrailing){
                    EditButton()
                }
                
                
            }
        }
    }
    
}

代码 - AddContactView

struct AddContactView: View {
    
    @State var name: String = ""
    @State var email: String = ""
    @State var isButtonpressed: Bool = false
    
    @EnvironmentObject var myContacts: Contacts
    
    var body: some View {
        
       
        vstack{
            
            HStack{
                Text("Name:")
                TextField("Enter name",text: $name)
            }
            .padding(.bottom,50)
            
            HStack{
                Text("Email:")
                TextField("Enter email",text: $email)
            }
            .padding(.bottom,50)
            
            
            Button("Submit") {
                
                let contactToAdd = Contact(name: name,email: email)
                
                //Add is a simple function - all it does is append an item to the    myContacts array using the .append method
                myContacts.add(contact: contactToAdd)
                isButtonpressed = true
            }

            .frame(width: 80,height: 30,alignment:.center)
            .background(Color.blue)
            .foregroundColor(.white)
            .clipShape(Capsule())
                
            NavigationLink(destination: ContentView().navigationBarHidden(true),isActive: $isButtonpressed,label: {
                       EmptyView()
                    }).hidden()
            
        }.padding()
            
        }
    
}

我的尝试

如果我注释掉 .add 方法并且不更新环境对象,则返回 ContentView 的导航将按预期工作。所以我知道这就是问题的具体原因。

我已经尝试将 .onTapGesture 修饰符添加到 Button 并在那里调用 .add。

我尝试向整个视图添加 .ondisappear 修饰符并在那里调用 .add 。

-- 任何有关解决此问题的帮助或澄清将不胜感激

编辑:屏幕录制 - 根据第一条评论尝试解决方案:

What happens when I try the solution

奇怪的行为:第一次尝试添加联系人自动路由回 AddContactView,产生相同的错误。但是如果我再试一次,它就会正确地路由到 ContactView。

解决方法

编辑更新。这是我用来测试我的答案的代码:

import SwiftUI

@main
struct TestApp: App {
    @StateObject var myContacts = Contacts()
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(myContacts)
        }
    }
}

struct Contact: Identifiable {
    var id = UUID()
    var name: String = ""
    var email: String = ""
}

class Contacts: ObservableObject {
    @Published var contacts: [Contact] = [Contact(name: "name1",email: "email1"),Contact(name: "name2",email: "email2")]
    
    func add(contact: Contact) {
        contacts.append(contact)
    }
}

struct AddContactView: View {
    @Environment(\.presentationMode) private var presentationMode
    
    @EnvironmentObject var myContacts: Contacts
    
    @State var name: String = ""
    @State var email: String = ""

    var body: some View {
        VStack{
            HStack{
                Text("Name:")
                TextField("Enter name",text: $name)
            }
            .padding(.bottom,50)
            HStack{
                Text("Email:")
                TextField("Enter email",text: $email)
            }
            .padding(.bottom,50)
            Button("Submit") {
                let contactToAdd = Contact(name: name,email: email)
                myContacts.add(contact: contactToAdd)
                presentationMode.wrappedValue.dismiss()
            }
            .frame(width: 80,height: 30,alignment:.center)
            .background(Color.blue)
            .foregroundColor(.white)
            .clipShape(Capsule())
        }.padding()
    }
}

struct ContentView: View {
    @EnvironmentObject var myContacts: Contacts
    @State var isAddButtonPressed: Bool = false
    
    var body: some View {
        NavigationView {
            List {
                ForEach(myContacts.contacts) { item in
                    NavigationLink(destination: AddContactView()) {
                        Text(item.name)
                    }
                }
            }
            .navigationBarTitle("Address Book")
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading){
                    Button(action: {
                        isAddButtonPressed.toggle()
                    },label: {
                        NavigationLink(
                            destination: AddContactView(),isActive: $isAddButtonPressed,label: {
                            Image(systemName: "plus")
                        })
                    })
                }
                ToolbarItem(placement: .navigationBarTrailing){
                    EditButton()
                }
            }
        }
    }
}