swift 如何在UIViewRepresentable中观察多个更改?

2cmtqfgy  于 2023-09-29  发布在  Swift
关注(0)|答案(1)|浏览(95)

继续我的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 gameService = GameService()
    
    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 {
                           gameService.updateUserScore()
                        }
                    }
                    .onChange(of: propA) { newValue in
                           gameService.doSomethingA()
                    }
                    .onChange(of: propB) { newValue in
                           gameService.doSomethingB()
                    }
                    .onChange(of: propZ) { newValue in
                           gameService.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.在gameService中创建一个包含上述要观察的属性的信封对象/模型,并将其作为绑定传递给UIViewRepresentable
1.让gameService观察这个被包围的物体,可能是使用Combine?但我不知道如何做到这一点,或者它是否会在所有的工作。任何帮助是赞赏如何有效地做到这一点。

dfuffjeb

dfuffjeb1#

您可以将所有@State var移动到您的GameService中,作为@Published var

class GameService: ObservableObject {
    @Published var someGameState = 1
    @Published var anotherGameState = 1

    func doSomething() {
        print(someGameState)
    }
    func doSomethingElse() {
        print(anotherGameState)
    }
}

然后,您可以通过@ObservedObject将其传递给GameViewRepresentable,以避免写出游戏视图所需的所有状态。

struct ContentView: View {
    @StateObject var game = GameService()
    
    var body: some View {
        GameViewRepresentable(game: game)
    }
}

struct GameViewRepresentable: UIViewRepresentable {
    @ObservedObject var game: GameService
    
    // ...
}

由于这是一个UIViewRepresentable,我认为您可能会通过其属性的setter来更新game(而不是说,从ObservedObject的投影值生成一个Binding)。在这种情况下,您可以使用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()
}

相关问题