在SwiftUI中组合异步返回值
我有两个来自两个不同类的异步返回值,一个来自HealthKit,另一个来自MotionManager。我的目标是组合这些值并在swiftui视图中输出它们,在该视图中它每秒刷新一次。我知道我必须在这里查看联合收割机框架,但我不知道从哪里开始。我找不到很多描述Swiftui+Combine的教程。我知道我必须查看.CombineTest,但我必须编写自己的发布者和订阅者,还是可以使用我这里的@Published property包装器(@Published var motionData=motionData()和@Published var heartRateValue:Double=0.0) 我的MotionManager类: 我的HealthKitManager课程: 我开始创建一个combinedViewModel,但我被困在这里,不知道这是否是一种方法:在SwiftUI中组合异步返回值,swiftui,combine,Swiftui,Combine,我有两个来自两个不同类的异步返回值,一个来自HealthKit,另一个来自MotionManager。我的目标是组合这些值并在swiftui视图中输出它们,在该视图中它每秒刷新一次。我知道我必须在这里查看联合收割机框架,但我不知道从哪里开始。我找不到很多描述Swiftui+Combine的教程。我知道我必须查看.CombineTest,但我必须编写自己的发布者和订阅者,还是可以使用我这里的@Published property包装器(@Published var motionData=motio
class CombinedViewModel: ObservableObject {
@Published var motionManager: MotionManager = MotionManager()
@Published var healthManager: HealthKitManager = HealthKitManager()
var anyCancellable: AnyCancellable?
init() {
anyCancellable = Publishers
.CombineLatest(motionManager.$motionValues,healthManager.$heartRateValue)
.sink(receiveValue: {
// Do something
}
})
}
}
我需要关注什么?我是否需要完全学习combine框架来编写我自己的发布者和订阅者,或者@Published有什么东西可以完成这项工作?或者我需要使用我的CombinedViewModel使用另一种方法吗
添加了contentView以供参考:
struct ContentView: View {
@State var isActive: Bool = false
private var motion = MotionManager()
private var health = HealthKitManager()
@ObservedObject var combinedViewModel = CombinedViewModel(managerOne: motion, managerTwo: health)
private var motionValues: MotionValues {
return combinedViewModel.combinedValues.0
}
private var heartRateValue: Double {
return combinedViewModel.combinedValues.1
}
var body: some View {
ScrollView {
VStack(alignment: .leading) {
Indicator(title: "X:", value: motionValues.rotationX)
Indicator(title: "Y:", value: motionValues.rotationY)
Indicator(title: "Z:", value: motionValues.rotationZ)
Divider()
Indicator(title: "Pitch:", value: motionValues.pitch)
Indicator(title: "Roll:", value: motionValues.roll)
Indicator(title: "Yaw:", value: motionValues.yaw)
Divider()
Indicator(title: "HR:", value: heartRateValue)
}
.padding(.horizontal, 10)
Button(action: {
self.isActive.toggle()
self.isActive ? self.start() : self.stop()
}) {
Text(isActive ? "Stop" : "Start")
}
.background(isActive ? Color.green : Color.blue)
.cornerRadius(10)
.padding(.horizontal, 5)
}.onAppear {
self.health.autorizeHealthKit()
}
}
private func start() {
self.motion.startMotionUpdates()
self.health.fetchHeartRateData(quantityTypeIdentifier: .heartRate)
}
private func stop() {
self.motion.stopMotionUpdates()
self.health.stopFetchingHeartRateData()
}
}
您可以在您的
CombinedViewModel
中创建一个新的发布者(我建议使用AnyPublisher
),该发布者将两者的输出结合起来。这是您的代码的简化版本,带有组合视图模型
:
class ManagerOne {
@Published var someValue = "Some Value"
}
class ManagerTwo {
@Published var otherValue = "Other Value"
}
class CombinedViewModel {
var combinedPublisher: AnyPublisher<(String, String), Never>
init(managerOne: ManagerOne, managerTwo: ManagerTwo) {
combinedPublisher = managerOne.$someValue
.combineLatest(managerTwo.$otherValue)
.eraseToAnyPublisher()
}
}
关于这一点的旁注:
@Published var motionManager: MotionManager = MotionManager()
@Published var healthManager: HealthKitManager = HealthKitManager()
由于这两个管理器都是类,
$motionManager
和$healthManager
仅在您将motionManager
或HealthKitManager
的新实例分配给它们时才会发出值。当任何一位经理的财产发生变化时都不会。我建议阅读这篇文章,这篇文章很好地解释了你需要什么:我不知道期货如何解决将两个@Published
财产的输出组合成一个新的@Published
财产的问题,我将阅读它并尝试学习一些东西:)你知道如何在我的contentView中实现这一点吗?我在我的原始帖子中添加了contentView,因为combinedViewModel
是一个@ObservedObject
视图应该在每次combinedValue
更改时重新渲染。可以在视图模型上访问该属性,并根据需要获取值。或者你可以订阅组合视图模型。$combinedValue我直接尝试了这个,但没有成功:private var motion=MotionManager()private var health=HealthKitManager()@observeedobject var combinedViewModel=combinedViewModel(managerOne:motion,managertw:health)是的,我会打开一个新的。您的代码对于我所问的部分似乎还可以:)Tyit现在可以工作了,但是希望有人检查我的代码。
class ManagerOne {
@Published var someValue = "Some Value"
}
class ManagerTwo {
@Published var otherValue = "Other Value"
}
class CombinedViewModel {
var combinedPublisher: AnyPublisher<(String, String), Never>
init(managerOne: ManagerOne, managerTwo: ManagerTwo) {
combinedPublisher = managerOne.$someValue
.combineLatest(managerTwo.$otherValue)
.eraseToAnyPublisher()
}
}
class CombinedViewModel: ObservableObject {
@Published var combinedValue: (String, String) = ("", "")
var cancellables = Set<AnyCancellable>()
init(managerOne: ManagerOne, managerTwo: ManagerTwo) {
managerOne.$someValue
.combineLatest(managerTwo.$otherValue)
.sink(receiveValue: { [weak self] combined in
self?.combinedValue = combined
})
.store(in: &cancellables)
}
}
@Published var motionManager: MotionManager = MotionManager()
@Published var healthManager: HealthKitManager = HealthKitManager()