获取 HealthKit 中每个日期的总步数

Posted

技术标签:

【中文标题】获取 HealthKit 中每个日期的总步数【英文标题】:Get total step count for every date in HealthKit 【发布时间】:2015-04-11 20:04:48 【问题描述】:

HealthKit 中记录每天的总步数的最佳方法是什么。 使用 HKSampleQuery 的方法 initWithSampleType(见下文),我可以使用 NSPredicate 设置查询的开始和结束日期,但该方法返回一个每天包含许多 HKQuantitySamples 的数组。

- (instancetype)initWithSampleType:(HKSampleType *)sampleType
                     predicate:(NSPredicate *)predicate
                         limit:(NSUInteger)limit
               sortDescriptors:(NSArray *)sortDescriptors
                resultsHandler:(void (^)(HKSampleQuery *query,
                                         NSArray *results,
                                         NSError *error))resultsHandler

我想我可以查询所有记录的步数并遍历数组并计算每天的总步数,但我希望有一个更简单的解决方案,因为会有数以千计的 HKSampleQuery 对象。有没有办法让 initWithSampleType 返回每天的总步数?

【问题讨论】:

【参考方案1】:

你应该使用HKStatisticsCollectionQuery:

NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *interval = [[NSDateComponents alloc] init];
interval.day = 1;

NSDateComponents *anchorComponents = [calendar components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear
                                                 fromDate:[NSDate date]];
anchorComponents.hour = 0;
NSDate *anchorDate = [calendar dateFromComponents:anchorComponents];
HKQuantityType *quantityType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];

// Create the query
HKStatisticsCollectionQuery *query = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:quantityType
                                                                       quantitySamplePredicate:nil
                                                                                       options:HKStatisticsOptionCumulativeSum
                                                                                    anchorDate:anchorDate
                                                                            intervalComponents:interval];

// Set the results handler
query.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection *results, NSError *error) 
    if (error) 
        // Perform proper error handling here
        NSLog(@"*** An error occurred while calculating the statistics: %@ ***",error.localizedDescription);
    

    NSDate *endDate = [NSDate date];
    NSDate *startDate = [calendar dateByAddingUnit:NSCalendarUnitDay
                                             value:-7
                                            toDate:endDate
                                           options:0];

    // Plot the daily step counts over the past 7 days
    [results enumerateStatisticsFromDate:startDate
                                  toDate:endDate
                               withBlock:^(HKStatistics *result, BOOL *stop) 

                                   HKQuantity *quantity = result.sumQuantity;
                                   if (quantity) 
                                       NSDate *date = result.startDate;
                                       double value = [quantity doubleValueForUnit:[HKUnit countUnit]];
                                       NSLog(@"%@: %f", date, value);
                                   

                               ];
;

[self.healthStore executeQuery:query];

【讨论】:

这看起来很棒。试图翻译成swift但没有成功。谁有翻译? @dstefanis 我把它翻译成了 swift。不知道是否为时已晚。干杯。 我不知道这是 HealthKit 中的错误还是什么,但这个查询永远不会为我返回我设备上的步数和距离。 本页代码:developer.apple.com/reference/healthkit/… 一切正常我只需添加 anchorComponents.minute = 330; // 添加 5:30 小时以获得正确的当天结果,包括今天。【参考方案2】:

移植到 Swift 而不依赖于 SwiftDate 库

    let calendar = NSCalendar.current
    let interval = NSDateComponents()
    interval.day = 1

    var anchorComponents = calendar.dateComponents([.day, .month, .year], from: NSDate() as Date)
    anchorComponents.hour = 0
    let anchorDate = calendar.date(from: anchorComponents)

    // Define 1-day intervals starting from 0:00
    let stepsQuery = HKStatisticsCollectionQuery(quantityType: stepsCount!, quantitySamplePredicate: nil, options: .cumulativeSum, anchorDate: anchorDate!, intervalComponents: interval as DateComponents)

    // Set the results handler
    stepsQuery.initialResultsHandler = query, results, error in
        let endDate = NSDate()
        let startDate = calendar.date(byAdding: .day, value: -7, to: endDate as Date, wrappingComponents: false)
        if let myResults = results
            myResults.enumerateStatistics(from: startDate!, to: endDate as Date)  statistics, stop in
            if let quantity = statistics.sumQuantity()
                let date = statistics.startDate
                let steps = quantity.doubleValue(for: HKUnit.count())
                print("\(date): steps = \(steps)")
                //NOTE: If you are going to update the UI do it in the main thread
                DispatchQueue.main.async 
                    //update UI components
                

            
             //end block
         //end if let
    
    healthStore?.execute(stepsQuery)

【讨论】:

【参考方案3】:

使用核心 Swift 类修改 @sebastianr 的答案,仅用于测试我只返回一天的步骤,一旦你有更多的天数,你可以创建一个日期和步数字典并返回它

func getStepCountPerDay(completion:@escaping (_ count: Double)-> Void)

    guard let sampleType = HKObjectType.quantityType(forIdentifier: .stepCount)
        else 
            return
    
    let calendar = Calendar.current
    var dateComponents = DateComponents()
    dateComponents.day = 1

    var anchorComponents = calendar.dateComponents([.day, .month, .year], from: Date())
    anchorComponents.hour = 0
    let anchorDate = calendar.date(from: anchorComponents)

    let stepsCumulativeQuery = HKStatisticsCollectionQuery(quantityType: sampleType, quantitySamplePredicate: nil, options: .cumulativeSum, anchorDate: anchorDate!, intervalComponents: dateComponents
    )

    // Set the results handler
    stepsCumulativeQuery.initialResultsHandler = query, results, error in
        let endDate = Date()
        let startDate = calendar.date(byAdding: .day, value: 0, to: endDate, wrappingComponents: false)
        if let myResults = results
            myResults.enumerateStatistics(from: startDate!, to: endDate as Date)  statistics, stop in
                if let quantity = statistics.sumQuantity()
                    let date = statistics.startDate
                    let steps = quantity.doubleValue(for: HKUnit.count())
                    print("\(date): steps = \(steps)")
                    completion(steps)
                    //NOTE: If you are going to update the UI do it in the main thread
                    DispatchQueue.main.async 
                        //update UI components
                    
                
             //end block
         //end if let
    
    HKHealthStore().execute(stepsCumulativeQuery)

【讨论】:

【参考方案4】:

这是目前适用于 Swift 2.0 的翻译,使用 SwiftDate 库。

    let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)
    let startDate = NSDate().beginningOfDay().oneWeekAgo()
    let interval = NSDateComponents()
    interval.day = 1

    let predicate = HKQuery.predicateForSamplesWithStartDate(startDate, endDate: NSDate(), options: .StrictStartDate)
    let query = HKStatisticsCollectionQuery(quantityType: type!, quantitySamplePredicate: predicate, options: [.CumulativeSum], anchorDate: NSDate().begginingOfDay(), intervalComponents:interval)

    query.initialResultsHandler =  query, results, error in


        let endDate = NSDate()
        let startDate = NSDate().beginningOfDay().oneWeekAgo()
        if let myResults = results
            myResults.enumerateStatisticsFromDate(startDate, toDate: endDate) 
                statistics, stop in

                if let quantity = statistics.sumQuantity() 

                    let date = statistics.startDate
                    let steps = quantity.doubleValueForUnit(HKUnit.countUnit())
                    print("\(date): steps = \(steps)")
                
            
        
    

    healthKitStore.executeQuery(query)

【讨论】:

我不认为提供需要额外库的 Swift 翻译是个好主意 - 但没有冒犯【参考方案5】:

我将我的包裹在一个完成块中(目标 -c)。我发现最好将查询的 startDate 设置为今天午夜的日期。希望这会有所帮助,请随时复制/粘贴以开始使用

-(void)fetchHourlyStepsWithCompletionHandler:(void (^)(NSMutableArray *, NSError *))completionHandler 
     NSMutableArray *mutArray = [NSMutableArray new];
     NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];

     NSDate *startDate = [calendar dateBySettingHour:0 minute:0 second:0 ofDate:[NSDate date] options:0];

     NSDate *endDate = [NSDate date]; // Whatever you need in your case
     HKQuantityType *type = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];

    // Your interval: sum by hour
     NSDateComponents *intervalComponents = [[NSDateComponents alloc] init];
     intervalComponents.hour = 1;

   // Example predicate
     NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionStrictStartDate];

     HKStatisticsCollectionQuery *query = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:type quantitySamplePredicate:predicate options:HKStatisticsOptionCumulativeSum anchorDate:startDate intervalComponents:intervalComponents];
query.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection *results, NSError *error) 
    [results enumerateStatisticsFromDate:startDate toDate:endDate
     withBlock:^(HKStatistics *result, BOOL *stop) 
         if (!result) 
             if (completionHandler) 
                 completionHandler(nil, error);
             
             return;
         
         
         HKQuantity *quantity = result.sumQuantity;
         
         NSDate *startDate = result.startDate;
       
         NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
         formatter.dateFormat = @"h a";
        
         NSString *dateString = [formatter stringFromDate:startDate]; 
         
         double steps = [quantity doubleValueForUnit:[HKUnit countUnit]];
         
         NSDictionary *dict = @@"steps" : @(steps),
                                @"hour" : dateString
                                ;
         
         [mutArray addObject:dict];
     ];
    
    if (completionHandler) 
        completionHandler(mutArray, error);
    
;
    [self.healthStore executeQuery:query];

【讨论】:

【参考方案6】:

使用更新的 Swift 2.0 和 SwiftDate 库。

let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)
let startDate = NSDate().beginningOfDay
let interval = NSDateComponents()
interval.day = 1

let predicate = HKQuery.predicateForSamplesWithStartDate(startDate, endDate: NSDate(), options: .StrictStartDate)
let query = HKStatisticsCollectionQuery(quantityType: type!, quantitySamplePredicate: predicate, options: [.CumulativeSum], anchorDate: NSDate().beginningOfDay, intervalComponents:interval)

query.initialResultsHandler =  query, results, error in


  let endDate = NSDate()
  let startDate = NSDate().beginningOfDay
  if let myResults = results
    myResults.enumerateStatisticsFromDate(startDate, toDate: endDate) 
      statistics, stop in

      if let quantity = statistics.sumQuantity() 

        let date = statistics.startDate
        let steps = quantity.doubleValueForUnit(HKUnit.countUnit())
        print("\(date): steps = \(steps)")
      
    
  

healthKitStore.executeQuery(query)

【讨论】:

以上是关于获取 HealthKit 中每个日期的总步数的主要内容,如果未能解决你的问题,请参考以下文章

如何获取以前日期的 HealthKit 总步数

如何仅查询 HealthKit 以获取给定日期的总“在床上”时间?

如何仅查询 HealthKit 以获取给定日期的总“在床上”时间?

iOS利用HealthKit框架从健康app中获取步数信息

如何从 HealthKit 中获取每日平均步数

iOS获取健康步数从加速计到healthkit