使用Combine和SwiftUI显示变化的值的最简洁方法是什么?

goh*_*tis 3 ios swift swiftui combine

我想把我的头围绕SwiftUI和Combine。我想用一个值使UI中的一些文本保持最新。在这种情况下,例如,这是设备的电池电量。

这是我的代码。首先,似乎这是完成我想做的事情的大量代码,所以我想知道如果没有其中的一些我是否可以做到。另外,该代码过去通常在夏天运行,但现在崩溃了,可能是由于SwiftUI和Combine的更改。

如何解决此问题以使其与当前版本的SwiftUI和Combine一起使用?而且,是否有可能减少此处的代码量以执行相同的操作?

import SwiftUI
import Combine

class ViewModel: ObservableObject {

    var willChange = PassthroughSubject<Void, Never>()

    var batteryLevelPublisher = UIDevice.current
        .publisher(for: \.batteryLevel)
        .receive(on: RunLoop.main)

    lazy var batteryLevelSubscriber = Subscribers.Assign(object: self,
                                                         keyPath: \.batteryLevel)

    var batteryLevel: Float = UIDevice.current.batteryLevel {
        didSet {
            willChange.send()
        }
    }

    init() {
        batteryLevelPublisher.subscribe(batteryLevelSubscriber)
    }
}

struct ContentView: View {

    @ObservedObject var viewModel = ViewModel()

    var body: some View {
        Text("\(Int(round(viewModel.batteryLevel * 100)))%")
    }
}
Run Code Online (Sandbox Code Playgroud)

ber*_*rni 5

将最小的工作示例粘贴到iPadOS Swift游乐场中。

基本上,这是与SwiftUI和Combine的最新更改保持一致的给定代码。

  • 对要在视图中观察的任何属性使用@Published属性包装器(文档:默认情况下,ObservableObject会合成一个objectWillChange发布者,该发布者会在其任何@Published属性更改之前发出更改的值。)。这样可以避免使用自定义设置器和objectWillChange。

  • cancellable是Publishers.Assign的输出。可用于手动取消订阅,并且为了最佳实践,它将存储在“ CancellableBag”中,从而在deinit上取消订阅。这种做法的灵感来自其他反应式框架,例如RxSwift和ReactiveUI。

  • 我发现没有打开电池电量通知,电池电量的KVO发布者只会发出一次-1.0。

// iPadOS playground
import SwiftUI
import Combine
import PlaygroundSupport

class BatteryModel : ObservableObject {
    @Published var level = UIDevice.current.batteryLevel
    private var cancellableSet: Set<AnyCancellable> = []

    init () {
        UIDevice.current.isBatteryMonitoringEnabled = true
        assignLevelPublisher()
    }

    private func assignLevelPublisher() {
        _ = UIDevice.current
            .publisher(for: \.batteryLevel)
            .assign(to: \.level, on: self)
            .store(in: &self.cancellableSet)
    }
}
struct ContentView: View {

    @ObservedObject var batteryModel = BatteryModel()

    var body: some View {
        Text("\(Int(round(batteryModel.level * 100)))%")
    }
}

let host = UIHostingController(rootView: ContentView())
PlaygroundPage.current.liveView = host

Run Code Online (Sandbox Code Playgroud)