在下面的代码中,作为填字游戏应用程序的一部分,我创建了一个Grid,它包含正方形、一个@Published正方形数组和一个GridView,用于显示Grid的实例,Grid。当我在GridView中更改网格的正方形时,GridView会像预期的那样重新创建,我会看到一个"quot;而不是";A";。
现在我又增加了一个级别——谜题包含一个@Published网格,网格。因此,在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())
}
}
从技术上讲,您可以将其添加到Puzzle
对象中:
var anyCancellable: AnyCancellable? = nil
init() {
self.anyCancellable = griddy.objectWillChange.sink(receiveValue: {
self.objectWillChange.send()
})
}
这是我在另一篇SO文章中发现的一个技巧,它会导致子对象的更改,从而触发父对象的objectWillChange
。
编辑:
我想注意的是,如果您更改子对象:
puzzle.griddy = someOtherGriddyObject
则您仍将订阅旧griddy
对象的更改,并且不会收到新的更新。
您可以可能通过在更改对象后更新可取消项来度过难关:
puzzle.griddy = someOtherGriddyObject
puzzle.anyCancellable = puzzle.griddy.objectWillChange.sink(receiveValue: {
puzzle.objectWillChange.send()
})
从这个方向思考:在MVVM SwiftUI概念中,每个引入的ObservableObject
实体都必须与相应的视图ObservedObject
配对(EnvironmentObject
与ObservedObject
相同,只是注入机制不同(,因此ViewModel中的特定更改将刷新相应的view,没有其他魔术/自动从模型到视图的更改传播。
所以如果Square
是ObservableObject
,那么应该有一些SquareView
,作为
struct SquareView {
@ObservedObject var vm: Square
...
因此,如果你的Puzzle
可观测包括Grid
可观测,那么对应的具有观察到的谜题的PuzzleView
应该包括具有观察到网格的GridView
,其中应该包括具有观测到的正方形的SqareView
。
Ie。
struct PuzzleView: View {
@EnvironmentObject var puzzle: Puzzle
var body: some View {
GridView().environmentObject(puzzle.griddy)
}
}
// ... and same for GridView, and so on...
这种方法产生了非常优化的更新,因为修改一个Square只会刷新一个相应的SquareView。
但是,如果在一个容器更新中累积所有平方。objectWillChange,那么修改一个平方会导致整个UI更新,这对UI流畅性和电池都是不利的
因为Grid是引用类型,griddy
发生突变时会触发@Published
。
class Puzzle: ObservableObject {
@Published var griddy: Grid = Grid()
}
注意,griddy
实际上并没有发生突变,因为它是一个参考。
但为什么它在@EnvironmentObject var griddy: Grid
中有效?
我的猜测是,SDK在@ObservableObject
中隐式地观察发布者。
但也许有人能提供更详细的答案。
我个人会避免长的发布链,而只是扁平化嵌套结构。