You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中如何在View中展示HKSampleQuery获取的心率数据

解决SwiftUI中HKSampleQuery心率数据无法展示到视图的问题

核心问题

你当前的代码里,latestHr是查询闭包内的局部变量,既无法被视图访问,也不能触发SwiftUI的视图更新。另外HKSampleQuery的回调是异步执行的,且可能在后台线程,直接更新UI会出问题。

解决方案步骤

  1. 定义状态变量存储心率数据:用@State(简单场景)或ObservableObject(更适合复杂逻辑)来保存心率值,让SwiftUI能监听变化并更新视图。
  2. 在异步回调中更新状态变量:注意切换到主线程更新UI,避免线程错误。
  3. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 19:10:49