代码之家  ›  专栏  ›  技术社区  ›  batman

如何观察UIViewRepresentable中的多个变化?

  •  1
  • batman  · 技术社区  · 2 年前

    从我的 previous question ,从 UIViewRepresentable ?以前,我只要求观察一个属性的更改 userWon 。然而,随着应用程序中引入了新功能,我现在需要观察新的变化。假设这些可能会增加更多,那么最好的方法是增加更多 State 属性,并将它们绑定到 UIViewRepresentable ?在这一点上,添加越来越多的绑定和 onChange 修改器以观察更改。 示例代码如下:

    struct GameView: View {
        @State private var userWon: Bool = false
        @State private var propA: Bool = false
        @State private var propB: Int = 0
        @State private var propC: Int = 0
        ...
        ...
        ...
        @State private var propZ: Int = 0
        @StateObject private var gameViewModel = GameViewModel()
        
        var body: some View {
            ZStack {
                VStack {
                    GameViewRepresentable(userWon: $userWon,
                                          propA: $propA,
                                          propB: $propB,
                                          propC: $propC,
                                          ...
                                          propZ: $propZ) // <- Add more bindings??
                        .onChange(of: userWon) { newValue in
                            if newValue {
                               gameViewModel.updateUserScore()
                            }
                        }
                        .onChange(of: propA) { newValue in
                               gameViewModel.doSomethingA()
                        }
                        .onChange(of: propB) { newValue in
                               gameViewModel.doSomethingB()
                        }
                        .onChange(of: propZ) { newValue in
                               gameViewModel.doSomethingZ()
                        }
                }
                .clipped()
            }
        }
    }
    
    struct GameViewRepresentable: UIViewRepresentable {
        typealias UIViewType = GameView
        @Binding var userWon: Bool
        
        func makeUIView(context: Context) -> GameView {
            let gameView = GameView()
            gameView.delegate = context.coordinator
            return gameView
        }
    
        func updateUIView(_ uiView: GameView, context: Context) {
        }
    
        func makeCoordinator() -> GameViewCoordinator {
            GameViewCoordinator(self)
        }
    
        class GameViewCoordinator: GameViewDelegate  {
            var parent: GameViewRepresentable
            
            init(_ parent: GameViewRepresentable) {
                self.parent = parent
            }
    
            func userWon() {
                self.parent.userWon = true
            }
        }
    }
    

    我能想到的另一种方法是:

    1. 在中创建封套对象/模型 gameViewModel 其包含上述待观察的特性,并将其作为与 UIViewRepresentable
    2. gameViewModel 观察这个被包裹的物体,可能使用 Combine ?但我不知道该怎么做,也不知道它是否有效。 对于如何有效地做到这一点,我们将不胜感激。
    1 回复  |  直到 2 年前
        1
  •  1
  •   Sweeper    2 年前

    你可以移动所有 @State var s喜欢你的 GameViewModel @Published var s

    class GameViewModel: ObservableObject {
        @Published var someGameState = 1
        @Published var anotherGameState = 1
    
        func doSomething() {
            print(someGameState)
        }
        func doSomethingElse() {
            print(anotherGameState)
        }
    }
    

    然后你可以把这个传给 GameViewRepresentable 通过 @ObservedObject ,以避免写出游戏视图所需的所有状态。

    struct ContentView: View {
        @StateObject var game = GameViewModel()
        
        var body: some View {
            GameViewRepresentable(game: game)
        }
    }
    
    struct GameViewRepresentable: UIViewRepresentable {
        @ObservedObject var game: GameViewModel
        
        // ...
    }
    

    由于这是 UIViewRepresentable ,我想你可能正在更新 game 通过其属性的setter(而不是说 Binding 根据 ObservedObject )。在这种情况下,您可以使用 didSet 要检测更改:

    @Published var someGameState = 1 {
        didSet { self.doSomething() }
    }
    @Published var anotherGameState = 1 {
        didSet { self.doSomethingElse() }
    }
    

    否则,可以使用 onReceive :

    // add .dropFirst() if you don't want the initial value to trigger this
    .onReceive(game.$someGameState) { _ in
        game.doSomething()
    }
    .onReceive(game.$anotherGameState) { _ in
        game.doSomethingElse()
    }