[本文翻译自hackingwithswift,点击链接阅读原文]
那么,我们实际上刚刚做了什么?
好吧,我们想要一种方法,让用户查看菜单中的项目并将其添加到订单中。但我们也希望他们订购的项目出现在应用程序的其他地方。
环境对象是SwiftUI在许多地方共享数据的方式,但它们本身并不是一个完整的解决方案,因为我们用户界面的不同部分很容易根据它们加载时间显示不同的东西。通过ObservableObject
协议,我们赋予了Order
类宣布它已更改的能力,我们现在可以让SwiftUI监视这些公告并重新加载UI。
我们刚刚创建了一个Order
实例,并将其放入环境中。因此,来自ContentView
的任何视图都可以读取该顺序并以某种方式操作它。
我们想从详细信息屏幕将项目添加到我们的订单中,因此请返回ItemDetail.swift并赋予它此属性:
@EnvironmentObject var order: Order
我们没有给它一个默认值,所以你可能会认为它会因为Swift的严格初始化规则而造成问题。然而,@EnvironmentObject
属性包装器有一些魔术:它允许这个变量在代码中没有值,因为我们说它已经在环境中设置好了。
当显示此视图时,SwiftUI将自动在其环境对象列表中查找具有Order
类型的东西,并将其附加到该属性中。如果找不到Order
对象,那么我们就有问题了:我们说过的东西没有,我们的代码会崩溃。这就像一个隐式未包装的可选选项,所以要小心。
@EnvironmentObject
是Swift中的另一个属性包装器,就像@Published
和@StateObject
一样。这意味着我们得到了我刚才提到的自动附加功能,但也告诉SwiftUI监视对象的任何更改,并在更改公告通过时刷新其用户界面。
在我们添加一些代码来操作ItemDetail
中的顺序之前,我们需要修复另一个预览问题。你看,我们现在承诺,当我们的ItemDetail
显示时,Order
类型的对象将出现在环境中,我们从iDineApp.swift创建并传递。当我们的应用程序真实运行时,这效果很好,但在Xcode预览中,我们不会从应用程序启动——我们是由视图文件末尾的PreviewProvider
代码创建的。
只有当我们处于调试模式时,才会构建此预览代码——当我们从Xcode构建时,而不是用于App Store。这意味着将仅与我们的预览相关的代码放入那里是安全的,在这种情况下,这将是一个临时Order
实例,以便它接收与实际运行时相同的数据:
struct ItemDetail_Previews: PreviewProvider {
static var previews: some View {
NavigationStack {
ItemDetail(item: MenuItem.example).environmentObject(Order())
}
}
}
这复制了我们在应用程序启动时的相同设置,这意味着我们的预览应该会再次工作。
现在,我们可以进行真正的交易:添加一个按钮,将我们当前的菜单项添加到订单中。SwiftUI中的按钮由两部分组成:一个标题字符串和一个包含点击按钮时运行的代码的动作闭包。
Order
类已经有一个add()
方法,该方法需要一个菜单项,因此我们将将其用于操作。至于标题,我们只需添加一些文字,上面写着“订购这个”——如果您愿意,欢迎您添加更多样式!
将此放入ItemDetail
的主体中,就在垫片之前:
Button("Order This") {
order.add(item: item)
}
.buttonStyle(.borderedProminent)
这就是向共享订单添加内容所需的一切,但我们实际上还看不到任何东西。
为了实现这一点,我们需要创建一个显示用户到目前为止订单的新屏幕,然后将其放入带有我们现有内容视图的标签栏中。
因此,按Cmd+N来制作一个新的SwiftUI视图,称其为“OrderView”。因为这需要与我们应用程序的其他部分具有相同的Order
实例,因此您需要赋予它与我们 gaveItemDetail相同的属性:
@EnvironmentObject var order: Order
以及其预览中的类似代码,以确保它也有效:
struct OrderView_Previews: PreviewProvider {
static var previews: some View {
OrderView().environmentObject(Order())
}
}
至于我们的OrderView
的正文,这些都是你已经知道的东西:
一个
List
视图,给我们一个滚动表。一些
Section
阻止我们拆分我们的信息。ForEach
和HStack
显示我们的订单项目数组,同时显示每个项目的名称和价格。结尾的第二个
Section
显示了下订单的导航链接。导航栏标题为“订单”。
把所有这些放在一起,给我们这个OrderView
结构:
struct OrderView : View {
@EnvironmentObject var order: Order
var body: some View {
NavigationStack {
List {
Section {
ForEach(order.items) { item in
HStack {
Text(item.name)
Spacer()
Text("$\(item.price)")
}
}
}
Section {
NavigationLink("Place Order") {
Text("Check out")
}
}
}
.navigationTitle("Order")
}
}
}
提示:当您只想为NavigationLink
一些文本时,您可以使用上面显示的更简单的初始化器,而不是提供label
闭包。
我们很快就会回到那个,但首先我们需要通过我们的用户界面访问它来确保它工作正常。
评论区