SwiftUI - 将数据传递给不同的视图

7
我正在开发一个有4个不同视图的应用程序。主视图(ContentView),一个AddView,一个EditView和一个独立的DataView类,我通过ObservableObject将所有数据传递给其他视图。
在主视图中,我有一个项目列表。在AddView中,我向该列表添加项目,并从ContentView中添加。我想通过使用导航链接来编辑已添加的项目。因此,从主视图中,我想进入EditView,更改值,然后再返回ContentView,在那里我可以看到更改后的值。
你会使用ObservableObject实现这一点吗?还是我需要EnvironmentObject?因为目前EditView无法正常工作,我无法将数据从ContentView传递到EditViewEditView上的所有文本字段都为空,值无法传递。从AddViewContentView传递数据可行,但是从ContentViewEditView却不行。
有人能告诉我数据如何与所有视图链接吗?

使用@EnvironmentObject。它允许所有添加了此对象的视图共享包含共享数据的同一对象。如果您想让我为您提供示例答案,请告诉我。 - George
如果您能为我的项目发布一个包含主视图、添加视图和编辑视图的示例,那就太好了。提前致谢! - Distace
1个回答

16

你应该使用 @EnvironmentObject。它允许对象被共享,这对于将数据共享到其他视图非常重要。

在这个例子中,我正在使用一个 Shopping 对象。这个应用程序将像购物清单一样运作。整个项目可以在 GitHub 上找到 这里

希望这很有用,因为花了相当长的时间。这只是一个关于如何在视图之间有效地使用 @EnvironmentObject通用示例。

应用程序看起来像这样:

应用程序外观


创建项目

(可以通过GitHub下载,参见上面的链接)

1: 首先,在您的 SceneDelegate.swift 中,将以下代码替换为:

let contentView = ContentView()

用:

let contentView = ContentView().environmentObject(Shopping())

2: 现在Xcode会抱怨Shopping还没有被创建,因此我们接下来会修复它:

class Shopping: ObservableObject {
    
    @Published var list = [
        ShoppingItem("Bread", quantity: 1),
        ShoppingItem("Milk", quantity: 2),
        ShoppingItem("Eggs", quantity: 12)
    ]
    
    func addItem(_ item: ShoppingItem) {
        list.append(item)
    }
}


class ShoppingItem: Identifiable {
    
    var name: String
    var quantity: Int
    
    init(_ name: String, quantity: Int) {
        self.name = name
        self.quantity = quantity
    }
}

3: 接下来,我们需要主要内容ContentView:

struct ContentView: View {

    @EnvironmentObject private var shopping: Shopping
    @State private var newItem: String?
    
    var body: some View {
        NavigationView {
            List {
                ForEach(shopping.list) { item in
                    NavigationLink.init(destination: EditView(currentItem: item)) {
                        HStack {
                            Text(item.name)
                            Spacer()
                            Text(String(item.quantity))
                            Spacer().frame(width: 10)
                        }
                    }
                }
                
                if newItem != nil {
                    TextField("New Item", text: $newItem.bound, onCommit: {
                        if !self.newItem!.isEmpty {
                            self.shopping.addItem(ShoppingItem(self.newItem!, quantity: 1))
                        }
                        self.newItem = nil
                    })
                }
            }
            .navigationBarTitle("Shopping List")
            .navigationBarItems(trailing: Button(action: {
                self.newItem = ""
            }, label: {
                Image(systemName: "plus.circle.fill")
                    .resizable()
                    .frame(width: 25, height: 25)
            }))
        }
    }
}

4:除此之外,还有这个扩展可以让可选的@State工作(参考这里,尽管这已经被简化了):

extension Optional where Wrapped == String {

    var bound: String {
        get {
            return self ?? ""
        }
        set {
            self = newValue
        }
    }
}

第5步:最后是EditView,允许您编辑购物清单中的项目名称:

struct EditView: View {

    let currentItem: ShoppingItem
    @EnvironmentObject private var shopping: Shopping
    @State private var name = ""
    
    var body: some View {
        TextField("Item", text: $name, onCommit: saveName)
            .padding()
            .background(Color.gray)
            .onAppear(perform: setName)
    }
    
    private func saveName() {
        shopping.objectWillChange.send()
        currentItem.name = name
    }
    private func setName() {
        name = currentItem.name
    }
}

非常感谢!这将帮助我完成我的应用程序 :) - Distace
@Distace 很高兴能帮到你! - George
我想感谢您为同事付出额外的努力,为他创建了一个特别的项目。向您致敬,先生! - Islombek Hasanov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接