在下面的代码中,它是填字游戏应用程序的一部分,我创建了一个 Grid ,其中包含 squares ,一个@Published Squares数组和一个要显示的GridView网格实例 griddy 。当我在GridView中更改griddy的正方形时,将按预期方式重新创建GridView,并且看到“!”而不是“ A”。
现在我又添加了一个级别-一个拼图包含一个@Published网格 griddy 。因此,在PuzzleView中,我使用puzzle.griddy。但是,这不起作用。字母没有变化,PuzzleView体内的断点永远不会被击中。
为什么?我正在开发一个真正需要这3种不同结构的应用程序。
import SwiftUI
class Square : ObservableObject {
@Published var letter: String
init(letter: String){
self.letter = letter
}
}
class Grid : ObservableObject {
@Published var squares:[[Square]] = [
[Square(letter: "A"), Square(letter: "B"), Square(letter: "C")],
[Square(letter: "D"), Square(letter: "E"), Square(letter: "F"), ]
]
}
class Puzzle: ObservableObject {
@Published var griddy: Grid = Grid()
}
struct GridView: View {
@EnvironmentObject var griddy: Grid
var body: some View {
VStack {
Text("\(griddy.squares[0][0].letter)")
Button("Change Numbers"){
griddy.squares[0][0] = Square(letter:"!")
}
}
}
}
struct PuzzleView: View {
@EnvironmentObject var puzzle: Puzzle
var body: some View {
VStack {
Text("\(puzzle.griddy.squares[0][0].letter)")
Button("Change Numbers"){
puzzle.griddy.squares[0][0] = Square(letter:"!")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
// PuzzleView().environmentObject(Puzzle())
GridView().environmentObject(Grid())
}
}
答案 0 :(得分:1)
从技术上讲,您可以将其添加到Puzzle
对象中
var anyCancellable: AnyCancellable? = nil
init() {
self.anyCancellable = griddy.objectWillChange.sink(receiveValue: {
self.objectWillChange.send()
})
}
这是我在another SO post中发现的一个技巧,它将导致子对象中的更改触发父对象的objectWillChange
。
我想指出的是,如果您更改子对象:
puzzle.griddy = someOtherGriddyObject
然后,您仍将订阅旧 griddy
对象的更改,并且您将不会收到新的更新。
通过更改对象后仅更新可取消对象,您可以可能:
puzzle.griddy = someOtherGriddyObject
puzzle.anyCancellable = puzzle.griddy.objectWillChange.sink(receiveValue: {
puzzle.objectWillChange.send()
})
答案 1 :(得分:1)
朝这个方向思考:在MVVM SwiftUI概念中,每个引入的ObservableObject
实体必须与相应的视图ObservedObject
配对(EnvironmentObject
与ObservedObject
相同,只是不同注入机制),因此 ViewModel 中的特定更改将刷新相应的 View ,模型之间没有其他 magic / automatic 传播。
因此,如果Square
是ObservableObject
,则应该有一些SquareView
,
struct SquareView {
@ObservedObject var vm: Square
...
因此,如果您Puzzle
可观察包括Grid
可观察,则相应的PuzzleView
已观察到的谜题应包括GridView
具有观察到的网格,其中应包括SqareView
观察到的正方形。
即。
struct PuzzleView: View {
@EnvironmentObject var puzzle: Puzzle
var body: some View {
GridView().environmentObject(puzzle.griddy)
}
}
// ... and same for GridView, and so on...
此方法可实现非常理想的更新,因为修改一个Square只会刷新一个对应的SquareView。
但是,如果您在一个容器更新中累积所有 Squares.objectWillChange ,然后修改一个Square就会导致UI更新,这很不好,而且对UI流畅度和电池消耗也是如此。
答案 2 :(得分:0)
我要说的是因为Grid是引用类型,并且@Published
发生突变时会触发griddy
。
class Puzzle: ObservableObject {
@Published var griddy: Grid = Grid()
}
请注意,griddy
实际上是不会突变的,因为它是参考。
但是为什么它可以在@EnvironmentObject var griddy: Grid
中工作?
我的猜测是,SDK会隐式观察@ObservableObject
中的发布者。
但是也许有人可以提供更好的详细答案。
我个人会避免较长的发布者链,而只是扁平化嵌套结构。