5

我是 SwiftUI 的新手,并且知道我可能需要以某种方式实现 EnvironmentObject,但我不确定在这种情况下如何实现。

这是Trade

class Trade {
    var teamsSelected: [Team]

    init(teamsSelected: [Team]) {
        self.teamsSelected = teamsSelected
    }
}

这是子视图。它有一个类的实例tradeTrade有一个将 1 附加到 array 的按钮teamsSelected

struct TeamRow: View {
    var trade: Trade

    var body: some View {
        Button(action: {
            self.trade.teamsSelected.append(1)
        }) {
            Text("Button")
        }
    }
}

这是父视图。如您所见,我trade进入了 child view TeamRow。我想trade与 in 同步,trade这样TeamRow我就可以传递trade.teamsSelectedTradeView.

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)
                    }
                }
            }
        }
    }
}
4

3 回答 3

9

为了让您更好地了解如何使用ObservableObject@ObservedObject@State@Binding.

前面要提的一件事 -@ObservedObject当前在运行 iOS 13 Beta 6、7 或 8 的物理设备上运行 SwiftUI 代码时出现故障。我在此处回答了一个更详细的问题,并解释了如何将@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可以选择编写自己的. 然后,当我想发布我的对象即将发生变化时,我可以调用for和。ObservableObjectPublisherCombinePassthroughSubjectself.objectWillChange.send()willSetnameteamsSelected

但是,此代码可以显着缩短。ObservableObject自动合成一个对象发布者,所以我们实际上不必自己声明它。我们还可以使用@Published来声明应该发送发布者事件而不是使用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,TeamRowTradeView. 请再次记住,我进行了一些更改(并添加了一个示例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, basic structs- 或简单值类型的集合。@ObservedObject用于符合 的对象,ObservableObject我们将其用于比Intor更复杂的数据结构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)

$字符表示我正在将绑定传递给文本字段。这意味着对我所做的任何更改TextFieldname将反映在我的Trade对象中。@Binding当您尝试在视图之间同步状态而不传递整个对象时,您可以通过声明允许您在视图之间传递绑定的属性来自己做同样的事情。

虽然我将您更改TradeView为 take @ObservedObject var trade,但您可以像这样简单地将teamsSelected您的交易视图作为绑定传递TradeView(teamsSelected: self.$trade.teamsSelected)- 只要您TradeView接受绑定。要配置您TradeView接受绑定,您所要做的就是像这样声明您的teamsSelected属性TradeView

@Binding var teamsSelected: [Team]

最后,如果您在使用物理设备时遇到问题,您可以在此处@ObservedObject参考我的回答,以了解如何将其用作解决方法。@EnvironmentObject

于 2019-08-23T09:34:17.403 回答
2

您可以在组合中使用@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)
    //...
}

或者,如果您@Publilshed的 viewModel( @ObservedObject) 中有一个属性,则使用它来绑定状态,如ChildView(property1: $viewModel.publishedProperty).

于 2020-10-08T05:54:40.970 回答
0

首先非常感谢 graycampbell 给了我更好的理解!但是,我的理解似乎并不完全奏效。我有一个稍微不同的情况,我无法完全解决。

我已经在一个单独的线程中问过我的问题,但我也想在此处添加它,因为它以某种方式符合主题:Read values from list of toggles in SwiftUI

也许你们中的某个人可以帮助我解决这个问题。如果这个主题与最初的帖子的主要区别是,我必须从每个视图中收集数据GameGenerationRowGameGenerationView然后将其交给另一个视图。

于 2020-03-25T10:50:01.747 回答