我采用了您的代码并更改了一些内容来说明 SwiftUI 的工作原理,以便让您更好地了解如何使用 ObservableObject、@ObservedObject、@State 和 @Binding。
首先要提一下 - @ObservedObject 当前在运行 iOS 13 Beta 6、7 或 8 的物理设备上运行 SwiftUI 代码时出现故障。我回答了一个问题 here,该问题在更多详细说明并解释如何使用@EnvironmentObject 作为解决方法。
我们先来看看Trade。由于您希望在视图之间传递Trade 对象,因此更改该Trade 对象的属性,然后将这些更改反映在使用该Trade 对象的每个视图中,您将需要制作Trade一个ObservableObject。我已经为您的 Trade 类添加了一个额外的属性,纯粹是为了说明,稍后我会解释。我将向您展示编写ObservableObject 的两种方法 - 首先是详细的方法,看看它是如何工作的,然后是简洁的方法。
import SwiftUI
import Combine
class Trade: ObservableObject {
let objectWillChange = PassthroughSubject<Void, Never>()
var name: String {
willSet {
self.objectWillChange.send()
}
}
var teamsSelected: [Int] {
willSet {
self.objectWillChange.send()
}
}
init(name: String, teamsSelected: [Int]) {
self.name = name
self.teamsSelected = teamsSelected
}
}
当我们符合ObservableObject 时,我们可以选择编写自己的ObservableObjectPublisher,我通过导入Combine 并创建PassthroughSubject 来完成。然后,当我想发布我的对象即将更改时,我可以调用self.objectWillChange.send(),就像我在willSet 上的name 和teamsSelected 一样。
但是,此代码可以大大缩短。 ObservableObject 自动合成一个对象发布者,所以我们实际上不必自己声明它。我们还可以使用@Published 来声明应该发送发布者事件的属性,而不是在willSet 中使用self.objectWillChange.send()。
import SwiftUI
class Trade: ObservableObject {
@Published var name: String
@Published var teamsSelected: [Int]
init(name: String, teamsSelected: [Int]) {
self.name = name
self.teamsSelected = teamsSelected
}
}
现在让我们看看您的TeamSelectView、TeamRow 和TradeView。请再次记住,我做了一些更改(并添加了一个示例 TradeView)只是为了说明一些事情。
struct TeamSelectView: View {
@ObservedObject var trade = Trade(name: "Name", teamsSelected: [])
@State var teams = [1, 1, 1, 1, 1]
var body: some View {
NavigationView{
VStack{
NavigationLink(destination: TradeView(trade: self.trade)) {
Text(self.trade.name)
}
List {
ForEach(self.teams, id: \.self) { team in
TeamRow(trade: self.trade)
}
}
Text("\(self.trade.teamsSelected.count)")
}
.navigationBarItems(trailing: Button("+", action: {
self.teams.append(1)
}))
}
}
}
struct TeamRow: View {
@ObservedObject var trade: Trade
var body: some View {
Button(action: {
self.trade.teamsSelected.append(1)
}) {
Text("Button")
}
}
}
struct TradeView: View {
@ObservedObject var trade: Trade
var body: some View {
VStack {
Text("\(self.trade.teamsSelected.count)")
TextField("Trade Name", text: self.$trade.name)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
}
}
我们先来看看@State var teams。我们将@State 用于简单值类型——Int、String、基本structs——或简单值类型的集合。 @ObservedObject 用于符合ObservableObject 的对象,我们将其用于比Int 或String 更复杂的数据结构。
您会注意到@State var teams 是我添加了一个导航栏项目,当按下时,它将在teams 数组中附加一个新项目,因为我们的List 是通过迭代该@ 生成的987654370@ 数组,每当按下按钮时,我们的视图都会重新渲染并向我们的List 添加一个新项目。这是您如何使用@State 的一个非常基本的示例。
接下来,我们有我们的@ObservedObject var trade。你会注意到我并没有真正做任何与你最初不同的事情。我仍在创建我的 Trade 类的实例并在我的视图之间传递该实例。但是由于它现在是ObservableObject,并且我们使用的是@ObservedObject,所以我们的视图现在都将在Trade 对象发生更改时接收发布者事件,并且会自动重新呈现它们的视图以反映这些更改。
我要指出的最后一件事是我在TradeView 中创建的TextField 以更新Trade 对象的name 属性。
TextField("Trade Name", text: self.$trade.name)
$ 字符表示我正在将绑定传递给文本字段。这意味着TextField 对name 所做的任何更改都将反映在我的Trade 对象中。您可以通过声明 @Binding 属性来自己做同样的事情,当您尝试在视图之间同步状态而不传递整个对象时,这些属性允许您在视图之间传递绑定。
虽然我将您的 TradeView 更改为 @ObservedObject var trade,但您可以简单地将 teamsSelected 作为绑定传递到您的交易视图 - TradeView(teamsSelected: self.$trade.teamsSelected) - 只要您的 TradeView 接受绑定即可。要配置您的TradeView 以接受绑定,您所要做的就是在TradeView 中声明您的teamsSelected 属性,如下所示:
@Binding var teamsSelected: [Team]
最后,如果您在物理设备上使用@ObservedObject 时遇到问题,您可以参考我的回答here,了解如何使用@EnvironmentObject 作为解决方法。