我是SwiftUI的新手,了解可能需要以某种方式实现EnvironmentObject,但我不确定在这种情况下如何实现。
这是Trade
类
class Trade {
var teamsSelected: [Team]
init(teamsSelected: [Team]) {
self.teamsSelected = teamsSelected
}
}
这是子视图。它具有trade
类的实例Trade
。有一个按钮将1附加到数组teamsSelected
。
struct TeamRow: View {
var trade: Trade
var body: some View {
Button(action: {
self.trade.teamsSelected.append(1)
}) {
Text("Button")
}
}
}
这是父视图。如您所见,我将trade
传递到子视图TeamRow
中。我希望trade
与trade
中的TeamRow
保持同步,以便随后将trade.teamsSelected
传递给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)
}
}
}
}
}
}
答案 0 :(得分:3)
我已采用您的代码,并做了一些更改以说明SwiftUI的工作方式,以便使您更好地了解如何使用ObservableObject
,@ObservedObject
,@State
和{ {1}}。
首先要提一件事-尝试在运行iOS 13 Beta 6、7或8的物理设备上运行SwiftUI代码时,@Binding
目前已损坏。我回答了一个问题here对此进行了更详细的介绍,并说明了如何使用@ObservedObject
作为解决方法。
首先让我们看一下@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
中使用self.objectWillChange.send()
。
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
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
数组生成的视图,只要按下按钮,我们的视图就会重新渲染并向teams
中添加一个新项目。这是如何使用List
的非常基本的示例。
接下来,我们有了@State
。您会注意到,我实际上没有做任何与您最初不同的事情。我仍在创建@ObservedObject var trade
类的实例,并在视图之间传递该实例。但是由于它现在是Trade
,并且我们使用的是ObservableObject
,所以只要@ObservedObject
对象发生更改,我们的视图现在都将收到发布者事件,并将自动重新渲染其视图以反映这些变化。
我要指出的最后一件事是我在Trade
中创建的TextField
,以更新TradeView
对象的Trade
属性。
name
TextField("Trade Name", text: self.$trade.name)
字符表示我正在将绑定传递给文本字段。这意味着$
对TextField
所做的任何更改都将反映在我的name
对象中。您可以声明Trade
属性来自己做同样的事情,这些属性允许您在尝试在视图之间同步状态而不传递整个对象时在视图之间传递绑定。
虽然我将您的@Binding
更改为TradeView
,但您可以将@ObservedObject var trade
像这样的绑定传递给您的交易视图-teamsSelected
-只要您的{ {1}}接受绑定。要将TradeView(teamsSelected: self.$trade.teamsSelected)
配置为接受绑定,只需要做的就是在TradeView
中声明您的TradeView
属性,如下所示:
teamsSelected
最后,如果在物理设备上使用TradeView
时遇到问题,可以参考我的答案here来获得有关如何使用@Binding var teamsSelected: [Team]
的解决方法的说明。
答案 1 :(得分:0)
首先,非常感谢Graycampbell给了我更好的理解!但是,我的理解似乎还没有完全解决。我的情况略有不同,我无法完全解决。
我已经在一个单独的线程中问过我的问题,但是我也想在这里添加它,因为它在某种程度上适合主题:Reading values from list of toggles in SwiftUI
也许你们当中有人可以帮助我。与本主题的初始帖子的主要区别在于,我必须从GameGenerationRow
中的每个GameGenerationView
收集数据,然后将其移交给另一个视图。
答案 2 :(得分:0)
您可以在合并中使用@Binding
和@State
/ @Published
。
换句话说,请在“子视图”中使用@Binding
属性,然后将其与“父视图”中的@State
或@Published
属性绑定。
struct ChildView: View {
@Binding var property1: String
var body: some View {
VStack(alignment: .leading) {
TextField(placeholderTitle, text: $property1)
}
}
}
struct PrimaryTextField_Previews: PreviewProvider {
static var previews: some View {
PrimaryTextField(value: .constant(""))
}
}
struct ParentView: View{
@State linkedProperty: String = ""
//...
ChildView(property1: $linkedProperty)
//...
}
或者如果您的viewModel(@Publilshed
)中具有@ObservedObject
属性,则可以使用它来绑定ChildView(property1: $viewModel.publishedProperty)
之类的状态。