SwiftUI 组合:嵌套观察对象

len*_*nny 6 swift swiftui combine observableobject

让 swiftUI 仍然基于嵌套观察对象进行更新的最佳方法是什么?

以下示例显示了我对嵌套观察对象的含义。球管理器的 balls 数组是一个已发布的属性,其中包含可观察对象的数组,每个对象本身都有一个已发布的属性(颜色字符串)。

不幸的是,当点击其中一个球时,它不会更新球的名称,也不会收到更新。那么我可能搞砸了在这种情况下联合收割机是如何工作的?

import SwiftUI

class Ball: Identifiable, ObservableObject {
    let id: UUID
    @Published var color: String
    init(ofColor color: String) {
        self.id = UUID()
       self.color = color
    }
}

class BallManager: ObservableObject {
    @Published var balls: [Ball]
    init() {
        self.balls = []
    }
}

struct Arena: View {
   @StateObject var bm = BallManager()

    var body: some View {
        VStack(spacing: 20) {
            ForEach(bm.balls) { ball in
                Text(ball.color)
                    .onTapGesture {
                        changeBall(ball)
                    }
            }
        }
        .onAppear(perform: createBalls)
        .onReceive(bm.$balls, perform: {
            print("ball update: \($0)")
        })
    }
    
    func createBalls() {
        for i in 1..<4 {
            bm.balls.append(Ball(ofColor: "c\(i)"))
        }
    }
    
    func changeBall(_ ball: Ball) {
        ball.color = "cx"
    }
}
Run Code Online (Sandbox Code Playgroud)

lor*_*sum 5

您只需创建一个BallView并观察它并从那里进行更改。你必须ObservableObject直接观察每一个

struct Arena: View {
    @StateObject var bm = BallManager()
    
    var body: some View {
        VStack(spacing: 20) {
            ForEach(bm.balls) { ball in
                BallView(ball: ball)
            }
        }
        .onAppear(perform: createBalls)
        .onReceive(bm.$balls, perform: {
            print("ball update: \($0)")
        })
    }
    
    func createBalls() {
        for i in 1..<4 {
            bm.balls.append(Ball(ofColor: "c\(i)"))
        }
    }
    
    
}
struct BallView: View {
    @ObservedObject var ball: Ball
    
    var body: some View {
        Text(ball.color)
            .onTapGesture {
                changeBall(ball)
            }
    }
    func changeBall(_ ball: Ball) {
        ball.color = "cx"
    }
}
Run Code Online (Sandbox Code Playgroud)


Geo*_*e_E 4

Ball当数组中的aballs发生变化时,可以调用objectWillChange.send()更新ObservableObject.

以下内容应该适合您:

class BallManager: ObservableObject {
    @Published var balls: [Ball] {
        didSet { setCancellables() }
    }
    let ballPublisher = PassthroughSubject<Ball, Never>()
    private var cancellables = [AnyCancellable]()
    
    init() {
        self.balls = []
    }
    
    private func setCancellables() {
        cancellables = balls.map { ball in
            ball.objectWillChange.sink { [weak self] in
                guard let self = self else { return }
                self.objectWillChange.send()
                self.ballPublisher.send(ball)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并通过以下方式进行更改:

.onReceive(bm.ballPublisher) { ball in
    print("ball update:", ball.id, ball.color)
}
Run Code Online (Sandbox Code Playgroud)

balls注意:如果传入的初始值并不总是空数组,则还应该调用setCancellables()init.