我的应用程序中有三个视图。第一个显示游戏列表。第二个有每个游戏的玩家列表。第三个有每个玩家的得分列表。所以结构看起来像这样:
struct GameView: View {
@environmentObject var model: Model
var body: some View {
NavigationView {
List(model.games) {game in
NavigationLink(destination: PlayerView(game: game)) {
// Some View
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
struct PlayerView: View {
@environmentObject var model: Model
var game Game
var gameIndex: Int {
model.games.firstIndex() {$0 == game}!
}
var body: some View {
TextField("Game", $model.games[gameIndex].title)
List(game.players) {player in
NavigationLink(destination: ScoreView(player: player, gameIndex: gameIndex)) {
// Some View
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
struct ScoreView: View {
@environmentObject var model: Model
var player: Player
var gameIndex: Int
var playerIndex: Int {
model.games[gameIndex].players.firstIndex() { $0 == player }!
}
var body: some View {
TextField("Player", $model.games[gameIndex].players[playerIndex].name)
List(player.scores) {score in
// Some View
}
}
}
Run Code Online (Sandbox Code Playgroud)
我的问题是:对于深入层次结构的每个视图,我必须一直返回到我的 environmentObject 并通过我的模型数组获取路径和索引,我必须通过每个视图。如果我更改 TextField 值(仅作为应用程序中每个数据更改的示例)并且我在 NavigationView 中前后移动,我希望每个视图都被更新。我敢肯定,存在一些错误的构造错误,但我没有得到正确的答案。
对于像你正在做的事情,我会使用ObservedObject
this 来将特定数据从一个视图传递到另一个视图 - 特别是当对象中的属性可能会更改并且需要做出反应时。
您拥有的输入很有意义,但我认为您应该只能使用输入而不是访问model
EnvironmentObject
. 例如,在PlayerView
代码中,您可以game.title
代替$model.games[gameIndex].title
. 问题是,正如您的代码一样,对游戏标题的更改不会反映出来,这就是ObservedObject
出现的地方。像 一样EnvironmentObject
,ObservedObject
s 会告诉 SwiftUI 在对象更新时更新视图。和EnvironmentObject
都ObservedObject
要求类符合相同的ObservableObject
协议(以前称为BindableObject
)。
您要做的主要更改是将ObservedObject
属性包装器添加到视图输入,例如game
和player
。如果还没有的话,您可能还需要添加ObservableObject
对Game
和类的一致性。Player
本文介绍了 SwiftUI 中不同类型的数据绑定,包括ObservedObject
.
可能导致更改不显示的一个可能的“问题”是,每个对象ObjectWillChange
不仅需要在其自己的值发生更改时发出事件,而且还需要在其任何子对象的值发生更改时发出事件。如果涉及到 s 数组,这就更复杂了ObservableObject
。
这是我在这种情况下使用的代码:
// an array of cancelables because we need to subscribe to every object in the players array.
var playerCans: [Cancellable]?
@Published var players: [Player] = [Player(), Player()] {
didSet {
// necessary because if the array gets set to something new,
// we want to notify of changes to the new array, not the old one
setupCans()
}
}
init() {
setupCans()
}
func setupCans() {
// Not sure if canceling is necessary, but doing it just in case
self.playerCans?.forEach({ (can) in
can.cancel()
})
self.playerCans = players.map { (player) in
return player.objectWillChange.sink {
self.objectWillChange.send()
}
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1156 次 |
最近记录: |