Swift中如何在View中展示HKSampleQuery获取的心率数据
解决SwiftUI中HKSampleQuery心率数据无法展示到视图的问题
核心问题
你当前的代码里,latestHr是查询闭包内的局部变量,既无法被视图访问,也不能触发SwiftUI的视图更新。另外HKSampleQuery的回调是异步执行的,且可能在后台线程,直接更新UI会出问题。
解决方案步骤
- 定义状态变量存储心率数据:用
@State(简单场景)或ObservableObject(更适合复杂逻辑)来保存心率值,让SwiftUI能监听变化并更新视图。 - 在异步回调中更新状态变量:注意切换到主线程更新UI,避免线程错误。
- 在VStack中绑定状态变量展示数据。
修改后的完整代码示例
import SwiftUI import HealthKit struct HeartRateView: View { // 用@State存储心率相关数据,SwiftUI会监听其变化自动更新视图 @State private var latestHeartRate: Double? @State private var startDateString: String = "" @State private var endDateString: String = "" // 初始化HealthStore实例 private let healthStore = HKHealthStore() var body: some View { VStack(spacing: 20) { // 根据心率数据是否存在展示不同内容 if let hr = latestHeartRate { Text("当前心率: \(Int(hr)) BPM") .font(.title) } else { Text("正在获取心率数据...") } Text("开始时间: \(startDateString)") Text("结束时间: \(endDateString)") } .onAppear { // 视图出现时触发心率查询 latestheartRate() } } func latestheartRate(){ guard let sampleType = HKObjectType.quantityType(forIdentifier: .heartRate) else { print("无法获取心率样本类型") return } let startDate = Calendar.current.date(byAdding: .month, value: -1, to: Date()) let predicate = HKQuery.predicateForSamples(withStart: startDate, end: Date(), options: .strictEndDate) let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false) let query = HKSampleQuery( sampleType: sampleType, predicate: predicate, limit: 1, // 只取最新一条数据,比HKObjectQueryNoLimit更高效 sortDescriptors: [sortDescriptor] ) { _, result, error in guard error == nil, let samples = result, !samples.isEmpty else { print("获取心率数据失败: \(error?.localizedDescription ?? "未知错误")") return } let data = samples[0] as! HKQuantitySample let unit = HKUnit(from:"count/min") let latestHr = data.quantity.doubleValue(for: unit) let dateFormatter = DateFormatter() dateFormatter.dateFormat = "HH:mm E, d MMM y" let startDateStr = dateFormatter.string(from: data.startDate) let endDateStr = dateFormatter.string(from: data.endDate) // 切换到主线程更新UI状态,避免线程错误 DispatchQueue.main.async { self.latestHeartRate = latestHr self.startDateString = startDateStr self.endDateString = endDateStr } print("Latest Hr\(latestHr) BPM // StartDate \(startDateStr): EndDate \(endDateStr)") } healthStore.execute(query) } func authorizeHealthKit() { guard HKHealthStore.isHealthDataAvailable() else { print("健康数据不可用") return } let readTypes: Set<HKObjectType> = [HKObjectType.quantityType(forIdentifier: .heartRate)!] healthStore.requestAuthorization(toShare: nil, read: readTypes) { success, error in if success { print("健康权限授权成功") } else { print("授权失败: \(error?.localizedDescription ?? "未知错误")") } } } init() { authorizeHealthKit() } }
关键修改点说明
- 新增
@State变量存储心率和时间数据,SwiftUI会自动监听这些变量的变化并刷新视图。 - 将查询的
limit设为1,因为只需要最新一条心率数据,提升查询效率。 - 在查询回调中用
DispatchQueue.main.async包裹状态变量的赋值,确保UI更新操作在主线程执行。 - 在
onAppear中调用查询方法,确保视图加载完成后立即获取数据。
内容的提问来源于stack exchange,提问作者monicho
相关产品推荐
相关产品推荐

