如何在 SwiftUI 中计算和发布数组

How to Calculate and Publish Arrays in SwiftUI

在 SwiftUI 中,如果我使用 属性 包装器 @Published 声明一个数组变量,然后在 for() 循环中计算该数组的每个元素,该变量是否会被发布每次我计算一个新元素?如果是这样,有没有办法告诉编译器在计算完每个元素之前不要发布变量?

我有一个应用程序可以为每个连续的音频数据帧计算频谱。然后,该应用程序将 spectrum[] 发布到呈现精美图形的多个视图。我希望每个视图只为每个连续的音频数据帧重新绘制一次 - 而不是为 spectrum[] 数组的每个计算元素重新绘制一次。

这里有一些简化的代码来说明这个问题:

class ArrayGenerator: ObservableObject {
    @Published var spectrum = [Float](repeating: 0.0, count: 1000) 

    DispatchQueue.main.async { [self] in
        for bin in 0 ..< 1000 {
            spectrum[bin] = (userGain + userSlope * Float(bin)) * amplitudes[bin]
        }
    }
}

由于这段代码更改变量 1,000 次,我相信它发布了 1,000 次。但我希望它只发布一次 - 当 for() 循环完成时。我怎样才能做到这一点?

您假设它将发布 1000 changes/renders 是不正确的。 for 循环的所有这些迭代将在主 运行 循环的 一次 迭代中完成。以这个简单的例子为例:

class ArrayGenerator: ObservableObject {
    @Published var spectrum = [Float](repeating: 0.0, count: 1000)

    func run() {
        DispatchQueue.main.async { [self] in
            for bin in 0 ..< 1000 {
                spectrum[bin] = Float(bin)
            }
        }
    }
}

struct ContentView: View {
    
    @StateObject private var generator = ArrayGenerator()
    
    var body: some View {
        let _ = print("Rendering...")
        Text(generator.spectrum.map { String([=10=]) }.joined())
            .onAppear {
                generator.run()
            }
    }
}

打印:

Rendering...
Rendering...

第一次出现时渲染一次,然后 onAppear 运行 次,循环完成后再渲染一次。

那么,您的代码已经达到了您的预期。

您假设它将发布 1000 个更改是正确的。 objectWillChange 是在 willSet 中发送的,尽管 SwiftUI 将这些通知合并到一个渲染器中,但这仍然是您想要避免的事情。解决这个问题的方法是简单地在数组的副本上进行工作,然后将已发布的 属性 设置为新版本的数组一次。