如何使用 Combine 的 CurrentValueSubject 并在 SwiftUI 视图中访问它?
Posted
技术标签:
【中文标题】如何使用 Combine 的 CurrentValueSubject 并在 SwiftUI 视图中访问它?【英文标题】:How to use Combine's CurrentValueSubject and access it in a SwiftUI View? 【发布时间】:2020-07-22 12:38:15 【问题描述】:我的理解是,Combine 中的 CurrentValueSubject 发布者适合按需访问,而不是发出一次值的常规发布者。因此,我尝试在环境对象中使用一个来存储 HKWorkout 中燃烧的总能量,以便在 SwiftUI 视图中完成锻炼后访问它。使用下面的代码,我得到了编译器错误Cannot convert return expression of type 'AnyCancellable' to return type 'Double'
,所以我认为我需要进行某种类型的转换但无法弄清楚?
class WorkoutManager: NSObject, HKWorkoutSessionDelegate, HKLiveWorkoutBuilderDelegate, ObservableObject
var finishedWorkoutTotalEnergyBurned = CurrentValueSubject<Double, Never>(0.0)
func stopWorkout()
self.finishedWorkoutTotalEnergyBurned.value = unwrappedWorkout.totalEnergyBurned!.doubleValue(for: .kilocalorie())
struct SummaryView: View
@StateObject var workoutManager = WorkoutManager()
var body: some View
Text("\(getFinishedWorkoutTotalEnergyBurned())")
.navigationBarHidden(true)
//.navigationTitle("Workout Complete")
func getFinishedWorkoutTotalEnergyBurned() -> Double
workoutManager.finishedWorkoutTotalEnergyBurned.sink(receiveValue: $0 )
【问题讨论】:
【参考方案1】:我认为您的问题表明您需要更多地了解 SwiftUI 和 Combine 背后的基本原理(关于“CurrentValueSubject vs regular Publisher”的说法是完全错误的)。
您只需在您的WorkoutManager
上公开一个@Published
属性,并在需要时将其设置为您想要的值:
class WorkoutManager: NSObject, HKWorkoutSessionDelegate, HKLiveWorkoutBuilderDelegate, ObservableObject
@Published var finishedWorkoutTotalEnergyBurned = 0.0
func stopWorkout()
finishedWorkoutTotalEnergyBurned = unwrappedWorkout.totalEnergyBurned!.doubleValue(for: .kilocalorie())
struct SummaryView: View
@StateObject var workoutManager = WorkoutManager()
var body: some View
Text("\(workoutManager.finishedWorkoutTotalEnergyBurned)")
.navigationBarHidden(true)
@Published
确实在后台使用了 Combine 发布者,它与 @StateObject
结合使用会导致视图更新。但是您需要在这里推理的是如何以及何时设置这些属性 - 并且视图将自动更新。在您展示的情况下,可能不需要直接使用发布者。
【讨论】:
我也应该说的是,锻炼实际上是在此之前的视图中完成的。这就是我所说的发布者触发一次(在创建 SummaryView 之前),所以我需要能够存储 finishedWorkoutTotalEnergyBurned 以便在完成锻炼并导航到 SummeryView 后可以访问它,这就是为什么我认为 CurrentValueSubject 可能工作。 如果完成,将值存储在某处(例如视图模型),然后直接显示 - 不需要发布者 对我的锻炼管理器,我认为是我的视图模型,所以我试图将它存储在那里并读取它 @GarySabo - 这听起来像是正确的整体方法。从你的问题中很难知道这些对象的生命周期是什么,所以听起来WorkoutManager
可能不是SummaryView
“拥有”的,所以它不是SummaryView
的StateObject
正确的方法。无论如何,在您确定这是您需要的并且根据您目前的使用情况,它不是之前,不要跳到组合发布者
我想通了,你是对的,SummaryView 没有通过带有数据的 WorkoutManager 的实例。我从@StateObject 更改为@EnvironmentObject private var workoutManager: WorkoutManager
并通过SummaryView().environmentObject(workoutManager)
在父视图中设置它,现在我可以从锻炼管理器中读取数据。 ?以上是关于如何使用 Combine 的 CurrentValueSubject 并在 SwiftUI 视图中访问它?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 SwiftUI 和 Combine 检测 Datepicker 的值变化?
如何使用 Combine 的 CurrentValueSubject 并在 SwiftUI 视图中访问它?
如何使用 Combine + Swift 复制 PromiseKit 风格的链式异步流