问题描述
我正在努力实现的目标:我是一名新的 SwiftUI 开发人员。我正在尝试构建一个简单的地址簿应用程序。我有三个观点:
- ContentView - 包含列表视图中所有联系人的主视图,导航视图顶部有一个添加联系人 ('+') 和编辑按钮
- AddContact 视图 - 具有“姓名”和“电子邮件”文本字段以及“提交”按钮
- 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()
}
}
}
}
}