【问题标题】:Heart rate monitor only app for watchOS 2 records extra calories and shows exercise仅适用于 watchOS 2 的心率监测器应用程序会记录额外的卡路里并显示锻炼情况
【发布时间】:2016-07-05 11:46:30
【问题描述】:

基本上,我正在开发一个监测心率的睡眠监测应用程序。所以,我不想开始任何锻炼活动,但我认为这就是苹果的工作方式!

这是我正在使用的仅心率代码:

@IBOutlet private weak var label: WKInterfaceLabel!
@IBOutlet private weak var deviceLabel : WKInterfaceLabel!
@IBOutlet private weak var heart: WKInterfaceImage!
@IBOutlet private weak var startStopButton : WKInterfaceButton!

let healthStore = HKHealthStore()

//State of the app - is the workout activated
var workoutActive = false

// define the activity type and location
var workoutSession : HKWorkoutSession?
let heartRateUnit = HKUnit(fromString: "count/min")
var anchor = HKQueryAnchor(fromValue: Int(HKAnchoredObjectQueryNoAnchor))


override func awakeWithContext(context: AnyObject?) {
    super.awakeWithContext(context)
}

override func willActivate() {
    super.willActivate()

    guard HKHealthStore.isHealthDataAvailable() == true else {
        label.setText("not available")
        return
    }

    guard let quantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate) else {
        displayNotAllowed()
        return
    }

    let dataTypes = Set(arrayLiteral: quantityType)
    healthStore.requestAuthorizationToShareTypes(nil, readTypes: dataTypes) { (success, error) -> Void in
        if success == false {
            self.displayNotAllowed()
        }
    }
}

func displayNotAllowed() {
    label.setText("not allowed")
}

func workoutSession(workoutSession: HKWorkoutSession, didChangeToState toState: HKWorkoutSessionState, fromState: HKWorkoutSessionState, date: NSDate) {
    switch toState {
    case .Running:
        workoutDidStart(date)
    case .Ended:
        workoutDidEnd(date)
    default:
        print("Unexpected state \(toState)")
    }
}

func workoutSession(workoutSession: HKWorkoutSession, didFailWithError error: NSError) {
    // Do nothing for now
    NSLog("Workout error: \(error.userInfo)")
}

func workoutDidStart(date : NSDate) {
    if let query = createHeartRateStreamingQuery(date) {
        healthStore.executeQuery(query)
    } else {
        label.setText("cannot start")
    }
}

func workoutDidEnd(date : NSDate) {
    if let query = createHeartRateStreamingQuery(date) {
        healthStore.stopQuery(query)
        label.setText("---")
    } else {
        label.setText("cannot stop")
    }
}

// MARK: - Actions
@IBAction func startBtnTapped() {
    if (self.workoutActive) {
        //finish the current workout
        self.workoutActive = false
        self.startStopButton.setTitle("Start")
        if let workout = self.workoutSession {
            healthStore.endWorkoutSession(workout)
        }
    } else {
        //start a new workout
        self.workoutActive = true
        self.startStopButton.setTitle("Stop")
        startWorkout()
    }

}

func startWorkout() {
    self.workoutSession = HKWorkoutSession(activityType: HKWorkoutActivityType.CrossTraining, locationType: HKWorkoutSessionLocationType.Indoor)
    self.workoutSession?.delegate = self
    healthStore.startWorkoutSession(self.workoutSession!)
}

func createHeartRateStreamingQuery(workoutStartDate: NSDate) -> HKQuery? {
    // adding predicate will not work
     // let predicate = HKQuery.predicateForSamplesWithStartDate(workoutStartDate, endDate: nil, options: HKQueryOptions.None)

    guard let quantityType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate) else { return nil }

    let heartRateQuery = HKAnchoredObjectQuery(type: quantityType, predicate: nil, anchor: anchor, limit: Int(HKObjectQueryNoLimit)) { (query, sampleObjects, deletedObjects, newAnchor, error) -> Void in
        guard let newAnchor = newAnchor else {return} 
        self.anchor = newAnchor
        self.updateHeartRate(sampleObjects)
    }

    heartRateQuery.updateHandler = {(query, samples, deleteObjects, newAnchor, error) -> Void in
        self.anchor = newAnchor!
        self.updateHeartRate(samples)
    }
    return heartRateQuery
}

func updateHeartRate(samples: [HKSample]?) {
    guard let heartRateSamples = samples as? [HKQuantitySample] else {return}

    dispatch_async(dispatch_get_main_queue()) {
        guard let sample = heartRateSamples.first else{return}
        let value = sample.quantity.doubleValueForUnit(self.heartRateUnit)
        self.label.setText(String(UInt16(value)))

        // retrieve source from sample
        let name = sample.sourceRevision.source.name
        self.updateDeviceName(name)
        self.animateHeart()
    }
}

func updateDeviceName(deviceName: String) {
    deviceLabel.setText(deviceName)
}

func animateHeart() {
    self.animateWithDuration(0.5) {
        self.heart.setWidth(60)
        self.heart.setHeight(90)
    }

    let when = dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * double_t(NSEC_PER_SEC)))
    let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    dispatch_after(when, queue) {
        dispatch_async(dispatch_get_main_queue(), {
            self.animateWithDuration(0.5, animations: {
                self.heart.setWidth(50)
                self.heart.setHeight(80)
            })
        })
    }
} }

总而言之,意想不到的观察结果是: 1. 我监测心率的时间有助于活动应用程序中的绿环。 2. 记录了意想不到的大量卡路里,即当人在床上或睡觉时!

能否请您提供正确的代码,帮助我在睡眠期间定期监控和显示一个人的心跳,而不会对绿环或额外的卡路里做出贡献。 ?

提前非常感谢!

【问题讨论】:

    标签: swift2 watchos-2 healthkit


    【解决方案1】:

    开始锻炼并运行心率监测器会在大约 6 小时后耗尽 Apple Watch 的电池电量(如果它已充满电),因此此时让它在睡觉时连续运行可能不太现实。

    据我所知,使用锻炼会话开始锻炼对您的应用有 2 项作用。它将您的应用程序保持在前台,并每隔几秒钟开始采集心率样本。有没有考虑不开始?您的健康包查询仍将按原样工作,心率监测器仍会每 15 分钟左右记录一次用户的心率。您放松的主要事情是将您的应用程序保持在前台,我想知道您是否需要这样做(因为用户会睡着)。

    从 healthkit 中检索最后一个心率样本:

    func getLatestHeartRate() {
        let quantityType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)!
        let sortDescriptor = NSSortDescriptor(key:HKSampleSortIdentifierStartDate, ascending: false)
        let sampleQuery = HKSampleQuery(sampleType: quantityType, predicate: nil, limit: 1, sortDescriptors: [sortDescriptor])
            { (sampleQuery, results, error ) -> Void in
    
        }
        self.healthStore.executeQuery(sampleQuery)
    }
    

    【讨论】:

    • 感谢您的回复。是否可以在将激活功能(即用户再次打开应用程序时)从健康应用程序中检索上次记录的心率?另外,如果应用程序不在前台,全局调度队列会死吗?我也注意到了睡眠时间,所以也需要这些数据!
    • 要从健康工具包中获取最后记录的心率值,您可以创建查询,按创建日期降序对心率进行排序,然后让它只返回第一项。全局调度队列和睡眠日期不应该消失 - 应用程序在后台暂停但不会被杀死,因此当它再次被带到前台时会保留内存。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-28
    • 1970-01-01
    • 2018-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多