[本文翻译自hackingwithswift,点击链接阅读原文]
我们希望人们通过选择物品并将其添加到购物车来下订单取货。我已经给了你一个专门的Order
类,它包含一系列项目,因此我们将向该类添加项目,然后在专用订单视图中显示它们。
但有一个陷阱:如果我们在ItemDetail
中添加内容,我们如何在完全独立的OrderView
中显示它们?更重要的是,随着事情的变化,我们如何确保这两个人相互更新?
嗯,SwiftUI有一个非常棒的解决方案,叫做环境对象。这些对象是我们的观点可以自由使用,但不能创建或管理——它们在其他地方创建,并在观点消失后继续存在。
在这个应用程序中,我们将在应用程序启动时创建一个订单实例,然后将其传递到我们的内容视图中。该内容视图中的任何视图——任何可以将内容视图称为其祖先的视图——都将自动访问该环境对象。更好的是,当任何视图更改它时,所有其他地点都会自动更新。
我们现在试试吧。打开您的iDineApp.swift,这是我们创建ContentView
初始实例的地方。现在给它这个属性:
@StateObject var order = Order()
提示:当您添加该行时,Xcode将显示错误,没关系——我们一会儿就会修复它。
当应用程序启动时,这会创建一个新的顺序,并保持它活跃,无论我们显示什么视图。@StateObject
属性包装器负责在我们应用程序的整个生命周期内保持对象的活力。
现在,我们可以在创建ContentView结构时将其传递到我们的ContentView
结构中——查找以下内容:
WindowGroup {
ContentView()
}
并用这个替换它:
WindowGroup {
ContentView()
.environmentObject(order)
}
现在,我说,当我们使用@StateObject
属性时,Xcode会抛出一个错误——类似“参数类型'Order'不符合预期类型'ObservableObject'”。
这意味着SwiftUI不了解其用户界面应该如何监视我们的Order
类的更改——它不了解如何发送和接收数据更改的通知。
想一想:如果我们从菜单中选择一些食物并将其添加到我们的订单中,我们希望它立即出现在订单页面上——我们不想点击刷新,或者等待几秒钟,我们希望立即。为了发挥作用,SwiftUI需要一种标准方式,让像Order
这样的对象说“嘿,如果有人在看着我,你应该知道我的数据刚刚更改了。”
这个标准已经存在,它是ObservableObject
协议。任何符合ObservableObject
的东西都可以在SwiftUI中使用,并在其值发生变化时发布公告,以便更新用户界面。
苹果提供了几种不同的发布更改公告的方式,但最简单的是在任何应该触发更改通知的属性之前使用@Published
属性包装器。在这种情况下,只需在属性之前放置@Published
就足以让它更新任何正在关注更改的SwiftUI视图——它真的很强大!
因此,打开Order.swift,并将items
属性更改为:
@Published var items = [MenuItem]()
就是这样!现在我们的类配置正确,我们可以让它符合ObservableObject
,如下:
class Order: ObservableObject {
...我们的代码又恢复了编译。总体而言,我们已经更新了Order
,以便它知道如何宣布对任何正在观看的视图的更改,我们已经告诉items
组,每当它发生变化时,它都应该发送这样的公告,我们在主应用程序中创建了Order
对象的实例,并将其放置在SwiftUI环境中供其他视图使用——很好!
评论区