且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2023-12-03 16:04:22

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

我在这个例子中使用了一个 Shopping 对象.这个应用程序就像一个购物清单.整个项目都可以在 GitHub


创建项目

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

1: 首先,在您的 SceneDelegate.swift 中,替换:

让 contentView = ContentView()

与:

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

2: Xcode 目前会抱怨 Shopping 尚未制作,所以我们接下来会解决这个问题:

class Shopping: ObservableObject {@Published var list = [ShoppingItem(面包",数量:1),ShoppingItem(牛奶",数量:2),ShoppingItem(鸡蛋",数量:12)]func addItem(_ item: ShoppingItem) {list.append(item)}}class ShoppingItem:可识别{变量名:字符串变量数量:整数init(_ name: String, 数量: Int) {self.name = 姓名self.quantity = 数量}}

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

struct ContentView: 查看 {@EnvironmentObject 私人 var 购物:购物@State 私有变量 newItem:字符串?var主体:一些视图{导航视图{列表 {ForEach(shopping.list) { item inNavigationLink.init(destination: EditView(currentItem: item)) {堆栈{文本(项目名称)垫片()文本(字符串(项目.数量))间隔().框架(宽度:10)}}}如果 newItem != nil {文本字段(新项目",文本:$newItem.bound,onCommit:{如果 !self.newItem!.isEmpty {self.shopping.addItem(ShoppingItem(self.newItem!, 数量: 1))}self.newItem = nil})}}.navigationBarTitle(购物清单").navigationBarItems(trailing: Button(action: {self.newItem = "";}, 标签: {图像(系统名称:plus.circle.fill").resizable().frame(宽:25,高:25)}))}}}

4:连同这个 extension 让可选的 @State 工作(信用 此处,虽然这已被简化):

extension 可选 where Wrapped == String {var 绑定:字符串 {得到 {回归自我??"}放 {自我 = 新价值}}}

5:最后 - EditView,允许您编辑购物清单中的商品名称:

struct EditView: View {让 currentItem:ShoppingItem@EnvironmentObject 私人 var 购物:购物@State 私有变量名 = "";var主体:一些视图{文本字段(项目",文本:$name,onCommit:saveName).填充().background(颜色.灰色).onAppear(执行:setName)}私人功能保存名称(){shopping.objectWillChange.send()currentItem.name = 名称}私人功能集名称(){名称 = currentItem.name}}

I am working on an App which has 4 different views. The main view (ContentView), an AddView, an EditView, and a separated DataView with a class where I pass all the data by an ObservableObject to the other views.

In the main view I have a list of items. In the AddView I add items to that list and from the ContentView. I would like to be able to edit the added items by using a navigation link. So from the main view I would like to go to EditView, change the values and go back to the ContentView again where I see the changed values.

Would you use ObservableObject for doing that or do I need EnvironmentObject? Because at the moment EditView is not working, I can't pass the data from ContentView to EditView, all the textfields on the EditView are empty, the values do not get passed over. It works to pass the data from AddView to ContentView but then not from ContentView to EditView.

Can someone tell me how the data has to be linked to all the views?

You should use @EnvironmentObject. It allows an object to be shared, which is very important for sharing data to other views.

I am using a Shopping object in this example. This app will act like a shopping list. This whole project is available of GitHub here.

I really hope this is useful, as it took quite a while. This is just a general example of how to use @EnvironmentObject effectively between Views.

The app looks like this:


Creating the project

(can be downloaded through GitHub, see above link)

1: First, in your SceneDelegate.swift, replace:

let contentView = ContentView()

with:

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

2: Xcode will be complaining for now about Shopping not being made yet, so we will fix that next:

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: Next, we want the main content, 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: Along with this extension to let optional @States work (credit here, although this has been simplified):

extension Optional where Wrapped == String {

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

5: And then finally - the EditView, to allow you to edit the name of the item in the shopping list:

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
    }
}