SwiftUI-YAPPNU(但另一个@Published属性未更新)

时间:2020-07-22 20:01:35

标签: swiftui publishing

在下面的代码中,它是填字游戏应用程序的一部分,我创建了一个 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())
    }
}

3 个答案:

答案 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配对(EnvironmentObjectObservedObject相同,只是不同注入机制),因此 ViewModel 中的特定更改将刷新相应的 View ,模型之间没有其他 magic / automatic 传播。

因此,如果SquareObservableObject,则应该有一些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中的发布者。

但是也许有人可以提供更好的详细答案。

我个人会避免较长的发布者链,而只是扁平化嵌套结构。