【问题标题】:SwiftUI - YAPPNU (Yet Another @Published Property Not Updating)SwiftUI - YAPPNU(另一个@Published 属性未更新)
【发布时间】:2020-11-12 11:21:06
【问题描述】:

在下面的代码中,作为填字游戏应用程序的一部分,我创建了一个 Grid,其中包含 squares、一个 Squares 的 @Published 数组和一个要显示的 GridView Grid 的一个实例,griddy。当我在 GridView 中更改 griddy 的方块时,GridView 会按预期重新创建,并且我看到一个“!”而不是“A”。

现在我又添加了一个级别——拼图包含一个@Published Grid,griddy。因此,在 PuzzleView 中,我使用了uzzle.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())
    }
}

【问题讨论】:

  • 这是一个略有不同的问题,但我的答案与那里相同:stackoverflow.com/a/62988407/968155。最简单的解决方案是将Square 设为值类型:struct Square { var letter: String }(不需要@PublishedObservableObject
  • 我需要 Square 成为一个 ObservableObject——它在 SquareView 中被观察——所以我需要它成为一个类。但感谢您的回复和想法。
  • 要被“观察”,它不需要是ObservableObject - 有一些范例,比如Bindings 在视图之间传递值。如果它有自己的生命周期事件(例如,值根据一些外部触发器更新),那么它成为一个可观察的对象是有意义的。无论如何,另一个答案告诉您如何处理ObservableObjects(那里提到的最后一种方法)

标签: swiftui publishing


【解决方案1】:

我会说是因为 Grid 是引用类型,而 @Published 会在 griddy 发生突变时触发。

class Puzzle: ObservableObject {
    @Published var griddy: Grid = Grid()
}

请注意,griddy 实际上并没有发生变异,因为它是一个引用。

但为什么它在 @EnvironmentObject var griddy: Grid 中有效?

我的猜测是 SDK 在 @ObservableObject 中隐式观察发布者。

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

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

【讨论】:

    【解决方案2】:

    往这个方向思考:在 MVVM SwiftUI 概念中,每个引入的ObservableObject 实体都必须与对应的视图ObservedObject 配对(EnvironmentObjectObservedObject 相同,只是注入机制不同),所以具体更改ViewModel 会刷新相应的View,没有其他magic/automatic 传播从模型到视图的变化。

    所以如果SquareObservableObject,应该有一些SquareView,如

    struct SquareView {
       @ObservedObject var vm: Square
       ...
    

    所以如果你Puzzle observable 包含Grid observable,那么对应的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 流畅性和电池都是不利的。

    【讨论】:

      【解决方案3】:

      从技术上讲,您可以将其添加到您的 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()
          })
      

      【讨论】:

        猜你喜欢
        • 2022-01-04
        • 2020-10-21
        • 1970-01-01
        • 2021-09-07
        • 2021-10-14
        • 2020-04-30
        • 2021-03-23
        • 1970-01-01
        • 2020-12-27
        相关资源
        最近更新 更多