美文网首页
关于iOS步数统计CMPedometer/Healthkit的使

关于iOS步数统计CMPedometer/Healthkit的使

作者: 我叫赵小贱 | 来源:发表于2018-12-30 16:57 被阅读41次

    最近项目新需求,要展示用户的步数,公里数,卡路里,运动时长,分别研究了一下CMPedometer/Healthkit这两个类。这两个类都是可以获取到当前用户的运动数据的。
    先说下CMPedometer
    首先要声明一个CMPedometer的属性,不声明属性你是得不到数据的(为什么稍后解释)上代码。

    @property (nonatomic, strong) CMPedometer *pedometer;
    
        _pedometer = [[CMPedometer alloc]init];
        NSCalendar *calendar = [NSCalendar currentCalendar];
        NSDate *now = [NSDate date];
        NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:now];
        // 开始日期
        NSDate *startDate = [calendar dateFromComponents:components];
        // 结束日期
        NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0];
        //判断记步功能
        if ([CMPedometer isStepCountingAvailable]) {
            [_pedometer queryPedometerDataFromDate:startDate toDate:endDate withHandler:^(CMPedometerData * _Nullable pedometerData, NSError * _Nullable error) {
                NSLog(@"%@===",error);//error.code 是105就是未开启权限
                NSLog(@"%@---",pedometerData);
            }];
    

    为什么要声明属性呢?查找了论坛也没发现为啥,不过我在后面加了这样一段代码之后不声明属性也可以用了

    //        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    //            NSLog(@"%@",pedometer);
    //        });
    //或者在回调里面打印一下pedpmeter也是可以用的
    

    感觉像是后面没有用到过CMPedometer被提前释放了,但是事实应该不是这样。我也不是很清楚,目前只调查到了这里,有后续发现在更新。

    下面说下Healthkit
    这个是苹果的健康应用中心,可以获取到健康数据中心的所有数据,运动步数,时长,距离,等等等。但是这个是可以通过三方写入数据的,所以要是想从Healthkit里面获取运动数据要考虑一下是不是也要统计三方写入的数据。不考虑三方的话也是可以去除只保留iPhone自身统计的。上代码

    //声明个属性
    @property (nonatomic, strong) HKHealthStore *healthStore;
    
        //查看healthKit在设备上是否可用,ipad不支持HealthKit
        if(![HKHealthStore isHealthDataAvailable])
        {
            NSLog(@"不支持healthKit");
        }
        //创建healthStore实例对象
        self.healthStore = [[HKHealthStore alloc] init];
        //设置需要获取的权限这里仅设置了步数
    //    HKObjectType *stepCount =
        NSSet *healthSet = [NSSet setWithObjects:[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount]
                            ,[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierActiveEnergyBurned],
                            [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning],nil];
        //从健康应用中获取权限
        [self.healthStore requestAuthorizationToShareTypes:nil readTypes:healthSet completion:^(BOOL success, NSError * _Nullable error) {
            if (success)
            {
                NSLog(@"获取步数权限成功");
                //获取步数后我们调用获取步数的方法
            }
            else
            {
                NSLog(@"获取步数权限失败");
    
            }
        }];
    

    这里强调一下!重点!在iOS12以下如果未开启权限是不知道的!因为任何失败success都是YES,这里的success是指设备有健康功能,error也是拿不到具体的原因是什么的。
    iOS12 以上用下方法就可以拿到是不是没有权限的问题

    - (void)getRequestStatusForAuthorizationToShareTypes:(NSSet<HKSampleType *> *)typesToShare
                                               readTypes:(NSSet<HKObjectType *> *)typesToRead
                                              completion:(void (^)(HKAuthorizationRequestStatus requestStatus, NSError * _Nullable error))completion API_AVAILABLE(ios(12.0), watchos(5.0));
    

    各位大佬也可以用这两种获取运动数据的方法结合使用
    以下是具体的获取步数与距离时间

    //查询数据
    - (void)readStepCount
    {
        //查询采样信息
        HKSampleType *sampleType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
        
        //NSSortDescriptors用来告诉healthStore怎么样将结果排序。
        NSSortDescriptor *start = [NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierStartDate ascending:NO];
        NSSortDescriptor *end = [NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierEndDate ascending:NO];
        NSCalendar *calendar = [NSCalendar currentCalendar];
        NSDate *now = [NSDate date];
        NSDate *startDate = [calendar startOfDayForDate:now];
        NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0];
        NSPredicate *pre = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionStrictStartDate];
    
        /*查询的基类是HKQuery,这是一个抽象类,能够实现每一种查询目标,这里我们需要查询的步数是一个
         HKSample类所以对应的查询类就是HKSampleQuery。
         下面的limit参数传1表示查询最近一条数据,查询多条数据只要设置limit的参数值就可以了
         */
        HKSampleQuery *sampleQuery = [[HKSampleQuery alloc] initWithSampleType:sampleType predicate:pre limit:HKObjectQueryNoLimit sortDescriptors:@[start,end] resultsHandler:^(HKSampleQuery * _Nonnull query, NSArray<__kindof HKSample *> * _Nullable results, NSError * _Nullable error) {
            //打印查询结果
            NSLog(@"%@",results);
            double steps = 0;
    
            //把结果装换成字符串类型
            for (HKQuantitySample *result in results) {
                NSLog(@"bundleIdentifier:%@",result.sourceRevision.source.bundleIdentifier);\
                //去除个人添加的数据
                if (![result.sourceRevision.source.bundleIdentifier containsString:appleHealth]) {
                    continue ;
                }
                NSString *startDate = Format(@"%ld",(long)[result.startDate timeIntervalSince1970]);
                NSString *endDate = Format(@"%ld",(long)[result.endDate timeIntervalSince1970]);
                
                //时间差
                NSString *countDate = Format(@"%ld",endDate.integerValue - startDate.integerValue);
                NSLog(@"运动时间==%@====%@ ====%@ ====%d",startDate,endDate,countDate,countDate.intValue/60);
                HKQuantity *quantity = result.quantity;
                HKUnit *heightUnit = [HKUnit countUnit];
                double usersHeight = [quantity doubleValueForUnit:heightUnit];
                steps += usersHeight;
    
            }
            
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                
                //查询是在多线程中进行的,如果要对UI进行刷新,要回到主线程中
                NSLog(@"最新步数:%.0f",steps);
            }];
            
        }];
        //执行查询
        [self.healthStore executeQuery:sampleQuery];
    }
    
    - (void)getDistancesFromHealthKit{
        HKQuantityType *stepType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];
        [self fetchSumOfSamplesTodayForType:stepType unit:[HKUnit meterUnit] completion:^(double stepCount, NSError *error) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"你的公里数为:%.f",stepCount);
    
            });
        }];
    }
    #pragma mark - 读取HealthKit数据
    - (void)fetchSumOfSamplesTodayForType:(HKQuantityType *)quantityType unit:(HKUnit *)unit completion:(void (^)(double, NSError *))completionHandler {
        NSCalendar *calendar = [NSCalendar currentCalendar];
        NSDate *now = [NSDate date];
        NSDate *startDate = [calendar startOfDayForDate:now];
        NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0];
        NSPredicate *pre = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionStrictStartDate];
    
        HKStatisticsQuery *query = [[HKStatisticsQuery alloc] initWithQuantityType:quantityType quantitySamplePredicate:pre options:HKStatisticsOptionSeparateBySource completionHandler:^(HKStatisticsQuery *query, HKStatistics *result, NSError *error) {
            HKQuantity *sum = nil;
            
            NSLog(@"result ==== %@",result.sources);
            for (HKSource *source in result.sources) {
                NSLog(@"%@",source.bundleIdentifier);
                if ([source.bundleIdentifier containsString:appleHealth]) {
                    sum = [result sumQuantityForSource:source];
                }
            }
            if (completionHandler) {
                double value = [sum doubleValueForUnit:unit];
                NSLog(@"sum ==== %@",sum);
                NSLog(@"value ===%f",value);
                
                completionHandler(value, error);
            }
        }];
        
        [self.healthStore executeQuery:query];
    }
    

    怎么去除三方写入的呢!通过抓取数据发现苹果自己统计的步数都会有“com.apple.health”开头标识(没有测试iWatch是不是也是这个开头,因为没有设备)。只要在整合数据的时候只统计“com.apple.health”开头的就好了。
    好了先写到这里。
    补充一下:更改手机系统时间,那么这个步数的获取也是不准的,所以当前时间应该从服务器获取,因为每次的网络请求的response里面有时间,大家谨记!

    相关文章

      网友评论

          本文标题:关于iOS步数统计CMPedometer/Healthkit的使

          本文链接:https://www.haomeiwen.com/subject/rwvvlqtx.html