ios - 使用组合和 SwiftUI 显示变化值的最简洁方法是什么?

标签 ios swift swiftui combine

我正在尝试了解 SwiftUI 和合并。我想让用户界面中的一些文本保持最新的值。例如,在本例中,它是设备的电池电量。

这是我的代码。首先,这似乎是实现我想做的事情的相当多的代码,所以我想知道我是否可以在没有其中一些的情况下完成。另外,这段代码过去可以运行整个夏天,但现在崩溃了,可能是由于 SwiftUI 和合并的变化。

如何修复此问题以与当前版本的 SwiftUI 和合并一起使用?而且,是否可以减少这里的代码量来完成同样的事情?

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)))%")
    }
}

最佳答案

要粘贴到 iPadOS Swift Playground 中的最小工作示例。

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

  • 对您想要在 View 中观察的任何属性使用 @Published 属性包装器(文档:默认情况下,ObservableObject 合成一个 objectWillChange 发布者,该发布者在其任何 @Published 属性更改之前发出更改的值。)。这避免了使用自定义 setter 和 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

关于ios - 使用组合和 SwiftUI 显示变化值的最简洁方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58572631/

相关文章:

iphone - 手动同步 iCloud

ios - 删除动态添加的文本字段 IOS

ios - Swift - 从 AppDelegate 添加 subview 到 View Controller

ios - libc++abi.dylib : terminating with uncaught exception of type NSException - Do I need to fix the Launch Screen or Viewcontroller?

ios - SwiftUI 列表行在点击后更改其背景颜色

swift - 更改 TabView 选项卡时如何运行函数

ios - 如何快速隐藏/显示按钮

ios - 我想在应用程序用户关闭套接字后立即检测断开连接事件

ios - let skView = view as 有什么用! SKView

macos - 如何使用 NSViewRepresentable 将 NSTextView 在 SwiftUI View 中垂直居中?