问题描述
我是SwiftUI的新手,了解可能需要以某种方式实现EnvironmentObject,但是我不确定在这种情况下如何实现.
I'm new to SwiftUI and understand that I may need to implement EnvironmentObject in some way, but I'm not sure how in this case.
这是 Trade
类
class Trade {
var teamsSelected: [Team]
init(teamsSelected: [Team]) {
self.teamsSelected = teamsSelected
}
}
这是子视图.它具有来自 Trade
类的实例 trade
.有一个按钮将1附加到 teamsSelected
数组.
This is the child view. It has an instance trade
from the Trade
class. There is a button that appends 1 to array teamsSelected
.
struct TeamRow: View {
var trade: Trade
var body: some View {
Button(action: {
self.trade.teamsSelected.append(1)
}) {
Text("Button")
}
}
}
这是父视图.如您所见,我将 trade
传递到子视图 TeamRow
中.我希望 trade
与 TeamRow
中的 trade
保持同步,以便随后将 trade.teamsSelected
传递给 TradeView
.
This is the parent view. As you can see, I pass trade
into the child view TeamRow
. I want trade
to be in sync with trade
in TeamRow
so that I can then pass trade.teamsSelected
to TradeView
.
struct TeamSelectView: View {
var trade = Trade(teamsSelected: [])
var body: some View {
NavigationView{
VStack{
NavigationLink(destination: TradeView(teamsSelected: trade.teamsSelected)) {
Text("Trade")
}
List {
ForEach(teams) { team in
TeamRow(trade: self.trade)
}
}
}
}
}
}
推荐答案
我已采用您的代码,并进行了一些更改以说明SwiftUI的工作方式,以便使您更好地了解如何使用 ObservableObject
, @ObservedObject
, @State
和 @Binding
.
I've taken your code and changed some things to illustrate how SwiftUI works in order to give you a better understanding of how to use ObservableObject
, @ObservedObject
, @State
, and @Binding
.
首先要提一件事-尝试在运行iOS 13 Beta 6、7或8的物理设备上运行SwiftUI代码时, @ObservedObject
当前已损坏.我回答了一个问题此处,其中对此进行了详细说明并说明了如何使用 @EnvironmentObject
作为解决方法.
One thing to mention up front - @ObservedObject
is currently broken when trying to run SwiftUI code on a physical device running iOS 13 Beta 6, 7, or 8. I answered a question here that goes into that in more detail and explains how to use @EnvironmentObject
as a workaround.
首先让我们看一下 Trade
.由于您要在视图之间传递 Trade
对象,因此请更改该 Trade
对象的属性,然后将这些更改反映在使用该 Trade的每个视图中
对象,您需要将 Trade
设置为 ObservableObject
.我已经为您的 Trade
类添加了一个额外的属性,纯粹出于说明目的,我将在后面解释.我将向您展示两种编写 ObservableObject
的方法-首先通过冗长的方式查看其工作方式,然后通过简洁的方式来实现.
Let's first take a look at Trade
. Since you're looking to pass a Trade
object between views, change properties on that Trade
object, and then have those changes reflected in every view that uses that Trade
object, you'll want to make Trade
an ObservableObject
. I've added an extra property to your Trade
class purely for illustrative purposes that I'll explain later. I'm going to show you two ways to write an ObservableObject
- the verbose way first to see how it works, and then the concise way.
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
.然后,当我要发布对象即将更改时,可以像在 name
和 teamsSelected
.
When we conform to ObservableObject
, we have the option to write our own ObservableObjectPublisher
, which I've done by importing Combine
and creating a PassthroughSubject
. Then, when I want to publish that my object is about to change, I can call self.objectWillChange.send()
as I have on willSet
for name
and teamsSelected
.
但是,此代码可以大大缩短. ObservableObject
自动合成对象发布者,因此我们实际上不必自己声明它.我们还可以使用 @Published
声明应该发送发布者事件的属性,而不是在 willSet
中使用 self.objectWillChange.send()
.
This code can be shortened significantly, however. ObservableObject
automatically synthesizes an object publisher, so we don't actually have to declare it ourselves. We can also use @Published
to declare our properties that should send a publisher event instead of using self.objectWillChange.send()
in willSet
.
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
),只是为了说明一些事情.
Now let's take a look at your TeamSelectView
, TeamRow
, and TradeView
. Keep in mind once again that I've made some changes (and added an example TradeView
) just to illustrate a couple of things.
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团队
.我们将 @State
用于简单值类型- Int
, String
,基本 struct
-或简单值类型的集合. @ObservedObject
用于符合 ObservableObject
的对象,我们将其用于比 Int
或 String更复杂的数据结构
.
Let's first look at @State var teams
. We use @State
for simple value types - Int
, String
, basic structs
- or collections of simple value types. @ObservedObject
is used for objects that conform to ObservableObject
, which we use for data structures that are more complex than just Int
or String
.
使用 @State var team
时,您会注意到的是,我添加了一个导航栏项目,该项目将在按下时将新项目添加到 teams
数组中,并且由于我们的 List
是通过遍历该 teams
数组生成的,因此只要按钮按下,我们的视图就会重新渲染并向 List
中添加一个新项目被按下.这是一个非常基本的示例,说明了如何使用 @State
.
What you'll notice with @State var teams
is that I've added a navigation bar item that will append a new item to the teams
array when pressed, and since our List
is generated by iterating through that teams
array, our view re-renders and adds a new item to our List
whenever the button is pressed. That's a very basic example of how you would use @State
.
接下来,我们进行 @ObservedObject var trade
.您会注意到,我并没有做任何与您最初不同的事情.我仍在创建我的 Trade
类的实例,并在我的视图之间传递该实例.但是由于它现在是一个 ObservableObject
,并且我们使用的是 @ObservedObject
,所以只要 Trade
对象发生更改,我们的视图现在都将收到发布者事件,将自动重新渲染其视图以反映这些更改.
Next, we have our @ObservedObject var trade
. You'll notice that I'm not really doing anything different than you were originally. I'm still creating an instance of my Trade
class and passing that instance between my views. But since it's now an ObservableObject
, and we're using @ObservedObject
, our views will now all receive publisher events whenever the Trade
object changes and will automatically re-render their views to reflect those changes.
我要指出的最后一件事是我在 TradeView
中创建的 TextField
,以更新 Trade
对象的名称代码>属性.
The last thing I want to point out is the TextField
I created in TradeView
to update the Trade
object's name
property.
TextField("Trade Name", text: self.$trade.name)
$
字符表示我正在将绑定传递给文本字段.这意味着 TextField
对 name
所做的任何更改都将反映在我的 Trade
对象中.通过声明 @Binding
属性,您可以自己做同样的事情,当您尝试在视图之间同步状态而不传递整个对象时,这些属性允许您在视图之间传递绑定.
The $
character indicates that I'm passing a binding to the text field. This means that any changes TextField
makes to name
will be reflected in my Trade
object. You can do the same thing yourself by declaring @Binding
properties that allow you to pass bindings between views when you are trying to sync state between your views without passing entire objects.
虽然我将您的 TradeView
更改为采用 @ObservedObject var trade
,但您可以像这样将绑定简单地将 teamsSelected
传递到您的交易视图- TradeView(teamsSelected:self.$ trade.teamsSelected)
-只要您的 TradeView
接受绑定.要将您的 TradeView
配置为接受绑定,只需要做的就是在 TradeView
中声明您的 teamsSelected
属性,如下所示:
While I changed your TradeView
to take @ObservedObject var trade
, you could simply pass teamsSelected
to your trade view as a binding like this - TradeView(teamsSelected: self.$trade.teamsSelected)
- as long as your TradeView
accepts a binding. To configure your TradeView
to accept a binding, all you would have to do is declare your teamsSelected
property in TradeView
like this:
@Binding var teamsSelected: [Team]
最后,如果在物理设备上使用 @ObservedObject
时遇到问题,则可以参考我的答案解释了如何使用 @EnvironmentObject
作为解决方法.
And lastly, if you run into issues with using @ObservedObject
on a physical device, you can refer to my answer here for an explanation of how to use @EnvironmentObject
as a workaround.
这篇关于如何在SwiftUI中随时从子视图作为父视图访问数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!