ios 步数不增加

2016-11-01 11:27:12 syg90178aw 阅读数 1800

iOS10之后需要在info.plist添加权限,以便访问HealthKit;

关于HealthKit获取步数,我写了一个类,以方便使用;

//  Created by 小广 on 2016/10/31.
//  Copyright © 2016年 All. All rights reserved.
//  健康数据的帮助类

#import <Foundation/Foundation.h>

typedef void(^SCHealthBlock)(BOOL success, NSString *stepCount);

@interface SCHealthHelper : NSObject

// 开启健康数据中心
- (void)setupHKHealthStore:(SCHealthBlock)block;

@end


//
//  SCHealthHelper.m
//  SuperCommunity
//
//  Created by 小广 on 2016/10/31.
//  Copyright © 2016年 All. All rights reserved.
//  健康数据的帮助类

#import "SCHealthHelper.h"
#import <HealthKit/HealthKit.h>

@interface SCHealthHelper ()

@property (nonatomic, strong) HKHealthStore *healthStore;
@property (nonatomic, copy) SCHealthBlock block;

@end

@implementation SCHealthHelper

// 开启健康数据中心
- (void)setupHKHealthStore:(SCHealthBlock)block {
    //查看healthKit在设备上是否可用,ipad不支持HealthKit
    if(![HKHealthStore isHealthDataAvailable]) {
        SCLog(@"设备不支持healthKit");
        block(NO, nil);
        return;
    }
    
    self.block = block;
    
    //创建healthStore实例对象
    self.healthStore = [[HKHealthStore alloc] init];
    
    //设置需要获取的权限这里仅设置了步数
    HKObjectType *stepCount = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
    
    NSSet *healthSet = [NSSet setWithObjects:stepCount, nil];
    
    //从健康应用中获取权限
    [self.healthStore requestAuthorizationToShareTypes:nil readTypes:healthSet completion:^(BOOL success, NSError * _Nullable error) {
        
        SCLog(@"error=error==error===%@==",error);
        if (success) {
            SCLog(@"获取步数权限成功");
            //获取步数后我们调用获取步数的方法
            [self readStepCount];
        } else {
            SCLog(@"获取步数权限失败");
        }
    }];
}

// 查询数据
- (void)readStepCount {
    //查询采样信息
    HKSampleType *sampleType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
    
    //NSSortDescriptors用来告诉healthStore怎么样将结果排序。
    NSSortDescriptor *start = [NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierStartDate ascending:NO];
    NSSortDescriptor *end = [NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierEndDate ascending:NO];
    
    /*查询的基类是HKQuery,这是一个抽象类,能够实现每一种查询目标,这里我们需要查询的步数是一个
     HKSample类所以对应的查询类就是HKSampleQuery。
     下面的limit参数传1表示查询最近一条数据,查询多条数据只要设置limit的参数值就可以了
     */
    
    @weakify(self);
    HKSampleQuery *sampleQuery = [[HKSampleQuery alloc] initWithSampleType:sampleType predicate:[self predicateForSamplesToday] limit:HKObjectQueryNoLimit sortDescriptors:@[start,end] resultsHandler:^(HKSampleQuery * _Nonnull query, NSArray<__kindof HKSample *> * _Nullable results, NSError * _Nullable error) {
        
        @strongify(self);
        NSString *stepStr = @"";
        BOOL success = NO;
        if(!error && results) {
            //打印查询结果
            SCLog(@"resultCount = %ld result = %@",results.count,results);
            if (results.count > 0) {
                NSInteger totleSteps = 0;
                for(HKQuantitySample *quantitySample in results) {
                    HKQuantity *quantity = quantitySample.quantity;
                    HKUnit *heightUnit = [HKUnit countUnit];
                    NSInteger usersHeight = (NSInteger)[quantity doubleValueForUnit:heightUnit];
                    totleSteps += usersHeight;
                }
                
                //把结果装换成字符串类型
                stepStr = [NSString stringWithFormat:@"%ld",(long)totleSteps];
                success = YES;
                SCLog(@"最新步数:%@",stepStr);
            }
        }
        
        if (self.block) {
            self.block(success, stepStr);
        }
    }];
    //执行查询
    [self.healthStore executeQuery:sampleQuery];
}


/*!
 *  获取当天的时间段
 *  @brief  当天时间段
 *
 *  @return 时间段
 */
- (NSPredicate *)predicateForSamplesToday {
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDate *now = [NSDate date];
    NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:now];
    [components setHour:0];
    [components setMinute:0];
    [components setSecond: 0];
    
    NSDate *startDate = [calendar dateFromComponents:components];
    NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0];
    NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionNone];
    return predicate;
}

@end



使用的时候

[self.healthHelper setupHKHealthStore:^(BOOL success, NSString *stepCount) {
        if (!success) {
            [SVProgressHUD showErrorWithStatus:@"未获取到健康数据,请开启健康数据共享"];
        }
    }];

就是有可能没有允许读取步数,即是success为false时,来个提示框,提醒一下用户去健康开启一下;


ps:上传AppStore的时候,健康HealthKit出现了问题,info.plist文件里 Required device capabilities的healthkit导致无法上传,

 NOTE

When you enable the HealthKit capabilities, Xcode adds HealthKit to the list of required device capabilities. This prevents users from purchasing or installing the app on devices that do not support HealthKit.

If HealthKit is not required for the correct operation of your app, you can open the app’s info.plist file and delete the healthkit entry from the Required device capabilities array.

For more information on required device capabilities, see the  UIRequiredDeviceCapabilities key in Information Property List Key Reference.

当您启用HealthKit能力,Xcode增加HealthKit到所需的设备功能的列表。这样可以防止用户在购买或不支持的设备HealthKit安装该应用程序。

如果不需要为您的应用程序的正确操作HealthKit,您可以打开应用程序的Info.plist文件,并删除所需的设备功能阵列healthkit项。

有关所需的设备功能的详细信息,请参阅信息属性列表键参考的UIRequiredDeviceCapabilities关键字。
解决方案,直接将info.plist文件里healthkit这个键值对删除即可;

参考文章:http://www.cnblogs.com/luoxiaofu/p/5259320.html

参考文章:http://www.cocoachina.com/ios/20140915/9624.html


2016-08-04 17:55:54 gf771115 阅读数 5070

转自:http://www.cnblogs.com/dongliu/p/5629065.html

计步模块接触了一年多,最近又改需求了,所以又换了全新的统计步数的方法,整理一下吧。

 

在iPhone5s以前机型因为没有陀螺仪的存在,所以需要用加速度传感器来采集加速度值信息,然后根据震动幅度让其加入踩点数组并过滤,获取自己需要的步数数据。

直接上代码吧:

首先需要一个步数的model如下:

复制代码
#import <Foundation/Foundation.h>

@interface VHSSteps : NSObject
//步数模型
@property(nonatomic,strong) NSDate *date;

@property(nonatomic,assign) int record_no;

@property(nonatomic, strong) NSString *record_time;

@property(nonatomic,assign) int step;

//g是一个震动幅度的系数,通过一定的判断条件来判断是否计做一步
@property(nonatomic,assign) double g;

@end
复制代码

然后是如何获取步数,首先判断传感器是否可用

复制代码
   //加速度传感器
    self.motionManager = [[CMMotionManager alloc] init];
    // 检查传感器到底在设备上是否可用
    if (!self.motionManager.accelerometerAvailable) {
        return;
    } else {
        // 更新频率是100Hz
        //以pull方式获取数据
        self.motionManager.accelerometerUpdateInterval = 1.0/40;
    }
复制代码

可用的话开始实现统计步数的算法

复制代码
 @try
    {
        //如果不支持陀螺仪,需要用加速传感器来采集数据
        if (!self.motionManager.isAccelerometerActive) {//  isAccelerometerAvailable方法用来查看加速度器的状态:是否Active(启动)。
            
            // 加速度传感器采集的原始数组
            if (arrAll == nil) {
                arrAll = [[NSMutableArray alloc] init];
            }
            else {
                [arrAll removeAllObjects];
            }
            
            /*
             1.push方式
             这种方式,是实时获取到Accelerometer的数据,并且用相应的队列来显示。即主动获取加速计的数据。
             */
            NSOperationQueue *queue = [[NSOperationQueue alloc] init];
            
            [self.motionManager startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData *accelerometerData, NSError *error){
                
                if (!self.motionManager.isAccelerometerActive) {
                    return;
                }
                
                //三个方向加速度值
                double x = accelerometerData.acceleration.x;
                double y = accelerometerData.acceleration.y;
                double z = accelerometerData.acceleration.z;
                //g是一个double值 ,根据它的大小来判断是否计为1步.
                double g = sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2)) - 1;
                
                //将信息保存在步数模型中
                VHSSteps *stepsAll = [[VHSSteps alloc] init];
                
                stepsAll.date = [NSDate date];
                
                //日期
                NSDateFormatter *df = [[NSDateFormatter alloc] init] ;
                df.dateFormat  = @"yyyy-MM-dd HH:mm:ss";
                NSString *strYmd = [df stringFromDate:stepsAll.date];
                df = nil;
                stepsAll.record_time =strYmd;
                
                stepsAll.g = g;
                // 加速度传感器采集的原始数组
                [arrAll addObject:stepsAll];
                
                // 每采集10条,大约1.2秒的数据时,进行分析
                if (arrAll.count == 10) {
                    
                    // 步数缓存数组
                    NSMutableArray *arrBuffer = [[NSMutableArray alloc] init];
                    
                    arrBuffer = [arrAll copy];
                    [arrAll removeAllObjects];
                    
                    // 踩点数组
                    NSMutableArray *arrCaiDian = [[NSMutableArray alloc] init];
                    
                    //遍历步数缓存数组
                    for (int i = 1; i < arrBuffer.count - 2; i++) {
                        //如果数组个数大于3,继续,否则跳出循环,用连续的三个点,要判断其振幅是否一样,如果一样,然并卵
                        if (![arrBuffer objectAtIndex:i-1] || ![arrBuffer objectAtIndex:i] || ![arrBuffer objectAtIndex:i+1])
                        {
                            continue;
                        }
                        VHSSteps *bufferPrevious = (VHSSteps *)[arrBuffer objectAtIndex:i-1];
                        VHSSteps *bufferCurrent = (VHSSteps *)[arrBuffer objectAtIndex:i];
                        VHSSteps *bufferNext = (VHSSteps *)[arrBuffer objectAtIndex:i+1];
                        //控制震动幅度,,,,,,根据震动幅度让其加入踩点数组,
                        if (bufferCurrent.g < -0.12 && bufferCurrent.g < bufferPrevious.g && bufferCurrent.g < bufferNext.g) {
                            [arrCaiDian addObject:bufferCurrent];
                        }
                    }
                    
                    //如果没有步数数组,初始化
                    if (nil == self.arrSteps) {
                        self.arrSteps = [[NSMutableArray alloc] init];
                        self.arrStepsSave = [[NSMutableArray alloc] init];
                    }
                    
                    // 踩点过滤
                    for (int j = 0; j < arrCaiDian.count; j++) {
                        VHSSteps *caidianCurrent = (VHSSteps *)[arrCaiDian objectAtIndex:j];
                        
                        //如果之前的步数为0,则重新开始记录
                        if (self.arrSteps.count == 0) {
                            //上次记录的时间
                            lastDate = caidianCurrent.date;
                            
                            // 重新开始时,纪录No初始化
                            record_no = 1;
                            record_no_save = 1;
                            
                            // 运动识别号
                            NSTimeInterval interval = [caidianCurrent.date timeIntervalSince1970];
                            NSNumber *numInter = [[NSNumber alloc] initWithDouble:interval*1000];
                            long long llInter = numInter.longLongValue;
                            //运动识别id
                            self.actionId = [NSString stringWithFormat:@"%lld",llInter];
                            
                            self.distance = 0.00f;
                            self.second = 0;
                            self.calorie = 0;
                            self.step = 0;
                            
                            self.gpsDistance = 0.00f;
                            self.agoGpsDistance = 0.00f;
                            self.agoActionDistance = 0.00f;
                            
                            caidianCurrent.record_no = record_no;
                            caidianCurrent.step = self.step;
                            
                            [self.arrSteps addObject:caidianCurrent];
                            [self.arrStepsSave addObject:caidianCurrent];
                            
                        }
                        else {
                            
                            int intervalCaidian = [caidianCurrent.date timeIntervalSinceDate:lastDate] * 1000;
                            
                            // 步行最大每秒2.5步,跑步最大每秒3.5步,超过此范围,数据有可能丢失
                            int min = 259;
                            if (intervalCaidian >= min) {
                                
                                if (self.motionManager.isAccelerometerActive) {
                                    
                                    //存一下时间
                                    lastDate = caidianCurrent.date;
                                    
                                    if (intervalCaidian >= ACCELERO_START_TIME * 1000) {// 计步器开始计步时间(秒)
                                        self.startStep = 0;
                                    }
                                    
                                    if (self.startStep < ACCELERO_START_STEP) {//计步器开始计步步数 (步)
                                        
                                        self.startStep ++;
                                        break;
                                    }
                                    else if (self.startStep == ACCELERO_START_STEP) {
                                        self.startStep ++;
                                        // 计步器开始步数
                                        // 运动步数(总计)
                                        self.step = self.step + self.startStep;
                                    }
                                    else {
                                        self.step ++;
                                    }
                                
                                    //步数在这里
                                    NSLog(@"步数%d",self.step);
                                    
                                    int intervalMillSecond = [caidianCurrent.date timeIntervalSinceDate:[[self.arrSteps lastObject] date]] * 1000;
                                    if (intervalMillSecond >= 1000) {
                                        
                                        record_no++;
                                        
                                        caidianCurrent.record_no = record_no;
                                        
                                        caidianCurrent.step = self.step;
                                        [self.arrSteps addObject:caidianCurrent];
                                    }
                                    
                                    // 每隔100步保存一条数据(将来插入DB用)
                                    VHSSteps *arrStepsSaveVHSSteps = (VHSSteps *)[self.arrStepsSave lastObject];
                                    int intervalStep = caidianCurrent.step - arrStepsSaveVHSSteps.step;
                                    
                                    // DB_STEP_INTERVAL 数据库存储步数采集间隔(步) 100步
                                    if (self.arrStepsSave.count == 1 || intervalStep >= DB_STEP_INTERVAL) {
                                        //保存次数
                                        record_no_save++;
                                        caidianCurrent.record_no = record_no_save;
                                        [self.arrStepsSave addObject:caidianCurrent];
                                        
                                        // 备份当前运动数据至文件中,以备APP异常退出时数据也不会丢失
                                        // [self bkRunningData];
                                        
                                    }
                                }
                            }
                            
                            // 运动提醒检查
                            // [self checkActionAlarm];
                        }
                    }
                }
            }];
            
        }
    }@catch (NSException * e) {
        NSLog(@"Exception: %@", e);
        return;
    }
复制代码

然后iPhone 5s出现了, 增加了 M7 运动协处理器,也带来了CMStepCounter类,从此我们就不用自己计算步数了,只要直接读取就好。

  首先还是要检测协处理器是否可用

复制代码
    if (!([CMStepCounter isStepCountingAvailable] || [CMMotionActivityManager isActivityAvailable])) {
        
        NSString *msg = @"demo只支持iPhone5s以上机型.";
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Opps!"
                                                        message:msg
                                                       delegate:nil
                                              cancelButtonTitle:@"OK"
                                              otherButtonTitles:nil];
        [alert show];
        
}
复制代码

然后才是获取步数的方法,主要有两种:

计步 第一种方法
     
     startStepCountingUpdatesToQueue:updateOn:withHandler:
     
     开始分发当前步数计数数据到第三方应用
     
     - (void)startStepCountingUpdatesToQueue:(NSOperationQueue *)queue updateOn:(NSInteger)stepCounts withHandler:(CMStepUpdateHandler)handler
     Parameters
     
     queue
     
     被指定执行特定的handler块的操作队列。第三方可以指定一个定制队列或者使用操作队列协助app的主线程。该参数不能为nil
     
     stepCounts
     
     记录的步伐数据,达到该数值去执行handler块。该数值必须大于0
     
     handler
     
     该块在步伐计数达到或超出数值时会被执行,该参数不能为nil。更多块方法信息参考CMStepQueryHandler。
     
     Discussion
     
     该方法实现对用户步伐数据的追踪,并周期性地唤起块方法去分发结果。当第三方调用了该方法,步伐计数器会重置当前步伐数为0,并开始计数。每次计数到达指定的步伐数时,会执行指定的handler块方法。比如,当设定stepCounts为100时,会在100,200,300等数目时发送更新,激活该块方法。每次发送到该块方法的步伐数目都是从你调用该方法开始的步伐数目总和。
     
     每次超过设定步数值时,指定的处理程序块handler会被执行。如果当超过设定值时第三方应用处在被挂起的状态,那程序块也不会被执行。当第三方应用被唤醒,程序块也不会执行,直到再次超过设定步数值。
     
     可以调用stopStepCountingUpdates方法去停止分发步数计数,当然当步数计数对像被销毁的时候,分发过程也会被停止。
    
  代码如下:

复制代码
  if ([CMStepCounter isStepCountingAvailable]) {
    self.stepCounter = [[CMStepCounter alloc] init];
    [self.stepCounter startStepCountingUpdatesToQueue:self.operationQueue
                                             updateOn:1
                                          withHandler:
     ^(NSInteger numberOfSteps, NSDate *timestamp, NSError *error) {

         dispatch_async(dispatch_get_main_queue(), ^{

             if (error) {
                 UIAlertView *error = [[UIAlertView alloc] initWithTitle:@"Opps!" message:@"error" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
                 [error show];
             }
             else {

                 NSString *text = [NSString stringWithFormat:@"当前步数: %ld", (long)numberOfSteps];
                 //这里是步数
                 weakSelf.stepsLabel.text = text;
             }
         });
     }];
}
复制代码

 

计步 第二种方法
    
     queryStepCountStartingFrom:to:toQueue:withHandler:
     
     收集并返回某一时间段内的历史步数数据
     
     - (void)queryStepCountStartingFrom:(NSDate *)start to:(NSDate *)end toQueue:(NSOperationQueue *)queuewithHandler:(CMStepQueryHandler)handler
     Parameters
     
     start
     
     收集步数数据的开始时间,该参数不能为 nil.
     
     end
     
     收集步数数据的停止时间,该参数不能为nil.
     
     queue
     
     执行指定handler块的操作队列,第三方可以指定一个定制队列或者使用操作队列协助app的主线程。该参数不能为nil
     
     handler
     
     执行处理结果的块方法,该参数不能为nil。更多块方法信息参考CMStepQueryHandler。
     
     Discussion
     
     该方法为异步方法,会立即返回并且把结果分发到指定的handler块中处理。系统最多仅存储最近7天内的有效步数数据。如果在指定时间范围内没有数据,则会传递一个0值到handler块中。
  
代码如下

复制代码
// 获取今日步数 
 __weak ViewController *weakSelf = self;
self.operationQueue = [[NSOperationQueue 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 ([CMStepCounter isStepCountingAvailable]) {
    [self.stepCounter  queryStepCountStartingFrom:startDate to:endDate toQueue:self.operationQueue withHandler:^(NSInteger numberOfSteps, NSError * _Nullable error) {
        NSLog(@"%ld",numberOfSteps);
        dispatch_async(dispatch_get_main_queue(), ^{
            if (error) {
                UIAlertView *error = [[UIAlertView alloc] initWithTitle:@"Opps!" message:@"error" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
                [error show];
            }
            else {
                weakSelf.totalLabel.text = [NSString stringWithFormat:@"今日总步数%ld",numberOfSteps];
            }
        });
    }];
}
复制代码

另外,iOS7还增加了CMMotionActivity类,用来获取运动状态

复制代码
  if ([CMMotionActivityManager isActivityAvailable]) {
    self.activityManager = [[CMMotionActivityManager alloc] init];
    [self.activityManager startActivityUpdatesToQueue:self.operationQueue
                                          withHandler:
     ^(CMMotionActivity *activity) {

         dispatch_async(dispatch_get_main_queue(), ^{

             NSString *status = [weakSelf statusForActivity:activity];
             NSString *confidence = [weakSelf stringFromConfidence:activity.confidence];

             weakSelf.statusLabel.text = [NSString stringWithFormat:@"状态: %@", status];
             weakSelf.confidenceLabel.text = [NSString stringWithFormat:@"速度: %@", confidence];
         });
     }];
}
复制代码
复制代码
- (NSString *)statusForActivity:(CMMotionActivity *)activity {
    
    NSMutableString *status = @"".mutableCopy;
    
    if (activity.stationary) {
        
        [status appendString:@"not moving"];
    }
    
    if (activity.walking) {
        
        if (status.length) [status appendString:@", "];
        
        [status appendString:@"on a walking person"];
    }
    
    if (activity.running) {
        
        if (status.length) [status appendString:@", "];
        
        [status appendString:@"on a running person"];
    }
    
    if (activity.automotive) {
        
        if (status.length) [status appendString:@", "];
        
        [status appendString:@"in a vehicle"];
    }
    
    if (activity.unknown || !status.length) {
        
        [status appendString:@"unknown"];
    }
    
    return status;
}

- (NSString *)stringFromConfidence:(CMMotionActivityConfidence)confidence {
    
    switch (confidence) {
            
        case CMMotionActivityConfidenceLow:
            
            return @"Low";
            
        case CMMotionActivityConfidenceMedium:
            
            return @"Medium";
            
        case CMMotionActivityConfidenceHigh:
            
            return @"High";
            
        default:
            
            return nil;
    }
}
复制代码

好吧,随着时间的推移,iOS8来了,也带来了healthkit,不过之前的方法满足需求也就还是用的CMStepCounter方法。

不过最近客户改需求了,手环,iWatch的数据也需要统计进来,就不得不用healthkit的方法了。

 还是老套路,先检查能不能用

 //查看healthKit在设备上是否可用,ipad不支持HealthKit
    if(![HKHealthStore isHealthDataAvailable])
    {
        NSLog(@"设备不支持healthKit");
    }

然后获取步数

复制代码
 //创建healthStore实例对象
    self.healthStore = [[HKHealthStore alloc] init];
    
    //设置需要获取的权限这里仅设置了步数
    HKObjectType *stepCount = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
    
    NSSet *healthSet = [NSSet setWithObjects:stepCount, nil];
    
    //从健康应用中获取权限
    [self.healthStore requestAuthorizationToShareTypes:nil readTypes:healthSet completion:^(BOOL success, NSError * _Nullable error) {
        if (success)
        {
            NSDateFormatter *formatter = [[NSDateFormatter alloc ]init];
            [formatter setDateFormat:@"yyyy-MM-dd"];
            NSDate *now = [NSDate date];
            NSString *todaystr = [formatter stringFromDate:now];
            NSDate *today = [formatter dateFromString:todaystr];
            
            NSDate *next = [today dateByAddingTimeInterval:24*60*60];
       //定义需要获取的数据为步数
            HKQuantityType *quantityType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
       //设置获取的步数时间间隔
       NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
            dateComponents.day = 1;
            
            NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:today endDate:next options:HKQueryOptionStrictStartDate];
            //创建查询统计对象collectionQuery
       HKStatisticsCollectionQuery *collectionQuery = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:quantityType quantitySamplePredicate:predicate options: HKStatisticsOptionCumulativeSum | HKStatisticsOptionSeparateBySource anchorDate:[NSDate dateWithTimeIntervalSince1970:0] intervalComponents:dateComponents];
            collectionQuery.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection * __nullable result, NSError * __nullable error) {
                float numberOfSteps = 0;
                for (HKStatistics *statistic in result.statistics) {
                    
                    for (HKSource *source in statistic.sources) {
                         //HKSource对象中的name可用于区分健康数据来源
                        if ([source.name isEqualToString:deviceName]) {
                            float steps = [[statistic sumQuantityForSource:source] doubleValueForUnit:[HKUnit countUnit]];
                            numberOfSteps += steps;
                            
                        }
                //deviceName是根据接入的设备做的标记,
                        if ([deviceName isEqualToString:@"iPhone"]) {
                            if ([source.name isEqualToString:[UIDevice currentDevice].name]) {
                                float steps = [[statistic sumQuantityForSource:source] doubleValueForUnit:[HKUnit countUnit]];
                                numberOfSteps += steps;
                                
                            }
                            
                        }else if ([deviceName isEqualToString:@"iWatch"] && ![source.name isEqualToString:[UIDevice currentDevice].name]){
                            if ([source.bundleIdentifier hasPrefix:@"com.apple.health"]) {
                                float steps = [[statistic sumQuantityForSource:source] doubleValueForUnit:[HKUnit countUnit]];
                                numberOfSteps += steps;
                                
                            }
                        }else if ([deviceName isEqualToString:@"xiaomi"]){
                            if ([source.name isEqualToString:@"小米运动"] || [source.bundleIdentifier isEqualToString:@"HM.wristband"]) {
                                float steps = [[statistic sumQuantityForSource:source] doubleValueForUnit:[HKUnit countUnit]];
                                numberOfSteps += steps;
                                
                            }
                            
                        }
                    }
                   
                }
                NSLog(@"ff = %f",numberOfSteps);
          //步数看这里就好
                stepString = [NSString stringWithFormat:@"%.0f",numberOfSteps];
            
                //CGFloat distance = [VHSCommon getDistance:numberOfSteps];
                //int calorie = [VHSCommon getActionCalorie:distance speed:distance * 3600 / 24*60*60];
                //distanceString = [NSString stringWithFormat:@"%.2f",distance];
                //calorString = [NSString stringWithFormat:@"%d",calorie];   
        
            };
            [self.healthStore executeQuery:collectionQuery];
        }
        else
        {
            NSLog(@"获取步数权限失败");
        }
    }];
复制代码

demo完整代码在这里:

加速度传感器进行计步

CMStepCounter获取健康步数

关于healthkit的还没有整理好,过段时间再补充吧。


2016-09-12 16:10:02 jq2530469200 阅读数 1288

/** 前段时间iOS 健康数据非常的火,我忍不住寂寞,写了一个博客:

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

 *

 *  1.第一步首先需要开启HealthKit

 *  TARGETS -> Capabilities -> HealthKit -> YES

 *

 *  在此目录栏下,有一个steps,会显示一个(图片如下:health.png)

 *  egadd the "HealthKit" entitlement to your app id

 *  这是 你可能需要Fix issue ,这时候可能会在项目中出现.entitlements 文件

 *  相关处理请自行查资料直到ok为止

 *

 *  2.新建一个HealthKitManage类,继承于NSObject

 *    (1)导入头文件

 *    #import <HealthKit/HealthKit.h>

 *    #import <UIKit/UIKit.h>

 *    #define HKVersion [[[UIDevice currentDevice] systemVersion] doubleValue]

 *    #define CustomHealthErrorDomain @"com.sdqt.healthError

 *  相关的方法请自行查看 HealthKitManage.h  & HealthKitManage.m  文件(已封装)

 *

 *  3.相关的调用

 *    HealthKitManage *manage = [HealthKitManage shareInstance];

 *    然后调用相关的方法(对象 方法名)

 *

 *  4.参考博客:http://www.jianshu.com/p/1dd6ad5b1520


 */


下面直接上代码:


#import <Foundation/Foundation.h>

#import <HealthKit/HealthKit.h>

#import <UIKit/UIKit.h>


@interface HealthKitManage : NSObject


@property(nonatomic,strong)HKHealthStore *healthStore;


/**

 *  2.创建单例

 */

+(id)shareInstance;


/*

 *  3.检查是否支持获取健康数据

 *  @brief  检查是否支持获取健康数据

 */

- (void)authorizeHealthKit:(void(^)(BOOL success, NSError *error))compltion;


/*

 *  4.获取步数

 */

- (void)getStepCount:(void(^)(double value, NSError *error))completion;


/**

 *  5.获取公里数

 *  读取步行+跑步距离

 */

- (void)getDistance:(void(^)(double value, NSError *error))completion;


@end



//

//  HealthKitManage.m

//  启动屏加启动广告页

//

//  Created by administrator on 16/9/12.

//  Copyright © 2016 WOSHIPM. All rights reserved.

//


#import "HealthKitManage.h"


/* 1.导入头文件 */

#define HKVersion [[[UIDevice currentDevice] systemVersion] doubleValue]

#define CustomHealthErrorDomain @"com.sdqt.healthError"



@implementation HealthKitManage


/**

 *  2.创建单例

 */

+(id)shareInstance

{

    static id manager ;

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        manager = [[[self class] alloc] init];

    });

    return manager;

}


/*

 *  3.检查是否支持获取健康数据

 *  @brief  检查是否支持获取健康数据

 */

- (void)authorizeHealthKit:(void(^)(BOOL success, NSError *error))compltion

{

    if(HKVersion >= 8.0) {

        if (![HKHealthStore isHealthDataAvailable]) {

            NSError *error = [NSError errorWithDomain: @"com.raywenderlich.tutorials.healthkit" code: 2 userInfo: [NSDictionary dictionaryWithObject:@"HealthKit is not available in th is Device"                                                                      forKey:NSLocalizedDescriptionKey]];

            if (compltion != nil) {

                compltion(false, error);

            }

            return;

        }

        if ([HKHealthStore isHealthDataAvailable]) {

            if(self.healthStore == nil)

                self.healthStore = [[HKHealthStore alloc] init];

            /*

             组装需要读写的数据类型

             */

            NSSet *writeDataTypes = [self dataTypesToWrite];

            NSSet *readDataTypes = [self dataTypesRead];

            

            /*

             注册需要读写的数据类型,也可以在健康”APP中重新修改

             */

            [self.healthStore requestAuthorizationToShareTypes:writeDataTypes readTypes:readDataTypes completion:^(BOOL success, NSError *error) {

                

                if (compltion != nil) {

                    NSLog(@"error->%@", error.localizedDescription);

                    compltion (success, error);

                }

            }];

        }

    }else {

        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"iOS 系统低于8.0"                                                                      forKey:NSLocalizedDescriptionKey];

        NSError *aError = [NSError errorWithDomain:CustomHealthErrorDomain code:0 userInfo:userInfo];

        compltion(0,aError);

    }

}


/*! 3.1

 *  @brief  写权限

 *  @return 集合

 *  设置需要获取的权限:

 */

- (NSSet *)dataTypesToWrite

{

    HKQuantityType *heightType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeight];

    HKQuantityType *weightType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];

    HKQuantityType *temperatureType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyTemperature];

    HKQuantityType *activeEnergyType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierActiveEnergyBurned];

    

    return [NSSet setWithObjects:heightType, temperatureType, weightType,activeEnergyType,nil];

}


/*! 3.2

 *  @brief  读权限

 *  @return 集合

 */

- (NSSet *)dataTypesRead

{

    HKQuantityType *heightType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeight];

    HKQuantityType *weightType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];

    HKQuantityType *temperatureType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyTemperature];

    HKCharacteristicType *birthdayType = [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierDateOfBirth];

    HKCharacteristicType *sexType = [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierBiologicalSex];

    HKQuantityType *stepCountType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];

    HKQuantityType *distance = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];

    HKQuantityType *activeEnergyType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierActiveEnergyBurned];

    

    return [NSSet setWithObjects:heightType, temperatureType,birthdayType,sexType,weightType,stepCountType, distance, activeEnergyType,nil];

}


/*

 * 4.获取步数

 */

- (void)getStepCount:(void(^)(double value, NSError *error))completion

{

    HKQuantityType *stepType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];

    

    /**  NSSortDescriptors用来告诉healthStore怎么样将结果排序。

     *

     *   NSSortDescriptor *start = [NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierStartDate ascending:NO];

     *

     */

    NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:NO];


    // Since we are interested in retrieving the user's latest sample, we sort the samples in descending order, and set the limit to 1. We are not filtering the data, and so the predicate is set to nil.

    /*查询的基类是HKQuery,这是一个抽象类,能够实现每一种查询目标,这里我们需要查询的步数是一个

     HKSample类所以对应的查询类就是HKSampleQuery

     下面的limit参数传1表示查询最近一条数据,查询多条数据只要设置limit的参数值就可以了

     */

    HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:stepType predicate:[HealthKitManage predicateForSamplesToday] limit:HKObjectQueryNoLimit sortDescriptors:@[timeSortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {

        if(error) {

            completion(0,error);

        } else {

            NSInteger totleSteps = 0;

            for(HKQuantitySample *quantitySample in results) {

                HKQuantity *quantity = quantitySample.quantity;

                HKUnit *heightUnit = [HKUnit countUnit];

                double usersHeight = [quantity doubleValueForUnit:heightUnit];

                totleSteps += usersHeight;

            }

            NSLog(@"当天行走步数 = %ld",(long)totleSteps);

            completion(totleSteps,error);

        }

    }];

    

    [self.healthStore executeQuery:query];/* 执行查询 */

}



/**

 *   5.获取公里数 

 *   读取步行+跑步距离

 */

- (void)getDistance:(void(^)(double value, NSError *error))completion

{

    HKQuantityType *distanceType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];

    NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:NO];

    HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:distanceType predicate:[HealthKitManage predicateForSamplesToday] limit:HKObjectQueryNoLimit sortDescriptors:@[timeSortDescriptor] resultsHandler:^(HKSampleQuery * _Nonnull query, NSArray<__kindof HKSample *> * _Nullable results, NSError * _Nullable error) {

        

        if(error){

            completion(0,error);

        }else{

            double totleSteps = 0;

            for(HKQuantitySample *quantitySample in results){

                HKQuantity *quantity = quantitySample.quantity;

                HKUnit *distanceUnit = [HKUnit meterUnitWithMetricPrefix:HKMetricPrefixKilo];

                double usersHeight = [quantity doubleValueForUnit:distanceUnit];

                totleSteps += usersHeight;

            }

            NSLog(@"当天行走距离 = %.2f",totleSteps);

            completion(totleSteps,error);

        }

    }];

    [self.healthStore executeQuery:query];

}


/*! 6.NSPredicate当天时间段的方法实现

 *  @brief  当天时间段

 *

 *  @return 时间段

 */

+ (NSPredicate *)predicateForSamplesToday {

    NSCalendar *calendar = [NSCalendar currentCalendar];

    NSDate *now = [NSDate date];

    NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:now];

    [components setHour:0];

    [components setMinute:0];

    [components setSecond: 0];

    

    NSDate *startDate = [calendar dateFromComponents:components];

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

    NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionNone];

    return predicate;

}


@end


附录:(一张图片)

2016-09-29 22:26:46 qq_30513483 阅读数 10217

现在很多软件都加上了运动模块,比如QQ和微信,而且还有排行榜,可以和好友比较谁的运动步数多,任何东西只要添加了比较功能,就变得不一样了。今天教大家用代码去修改QQ上的运动步数,修改完效果是这样的:


屏幕快照 2016-06-21 下午5.02.56.png
本帖子是抱着学习的心态来看待这个功能的,如果不喜欢请略过。
1准备工作

你需要一个iOS程序员所需要的设备:一个mac系统的电脑、一个Xcode、一个开发者账号(用开发者账号这是因为要使用HealthKIt框架,就必须开启授权)、还有一个苹果手机.

2新建项目

既然你都有开发者账号了,这我就只介绍重要的东西了,像怎么新建项目就不说了。
1.添加HealthKit.famework库。
2.开启授权,将Healthkit开关打开.如图:


786E95C6-E1E0-4272-BFBF-D19FBF4CE9AB.png


打开开关后会自动下载一个文件是后缀是:.entitlement的文件,如上图文件目录。

3 上代码

1引入头文件

#import <HealthKit/HealthKit.h>

2初始化 HKHealthStore
HKHealthStore —— 关键类(使用HealthKit框架必须创建该类)

 self.healthStore = [[HKHealthStore alloc] init];

3发出具体的请求许可,这里我们只需要写入步数就可以。
创建步数类型

HKQuantityType *stepCountType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
 NSSet *writeDataTypes = [NSSet setWithObjects:stepCountType,  nil];

发出具体的请求许可

[self.healthStore requestAuthorizationToShareTypes:writeDataTypes readTypes:readDataTypes completion:^(BOOL success, NSError *error) {

            if (!success) {
              //失败了
                return;
            }

        }];

程序运行到这就回弹出健康的那个提示界面了,选择允许选项。

4 设置步数并且保存

//数据看类型为步数.
    HKQuantityType *quantityTypeIdentifier = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];

    //表示步数的数据单位的数量
    HKQuantity *quantity = [HKQuantity quantityWithUnit:[HKUnit countUnit] doubleValue:100];

    //数量样本.
    HKQuantitySample *temperatureSample = [HKQuantitySample quantitySampleWithType:quantityTypeIdentifier quantity:quantity startDate:[NSDate date] endDate:[NSDate date] metadata:nil];

    //保存
    [self.healthStore saveObject:temperatureSample withCompletion:^(BOOL success, NSError *error) {
        if (success) {
            //保存成功
        }else {
            //保存失败
        }
    }];

那个100就是增加的步数了。
然后打开手机里的健康应用,你会发现步数增加了。再打开QQ里的运动,步数也增加了。理论上来说只要软件的步数是从健康应用里获取的就都可以改变,不只是QQ哦!!!!!

github下载地址
赶快去试试吧

感谢观看,欢迎评论和批评



文/idage(简书作者)
原文链接:http://www.jianshu.com/p/3a3a800d25c4
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
2016-01-08 17:57:23 u014220518 阅读数 5195

<span style="font-size:18px;">#pragma mark - 获取步数从HealthKit
- (void)getstepCountFromHealthKit
{
    HKQuantityType *type = self.items[5];
       //这个函数是获取苹果健康里面的全部步数包括人为添加的
    [self fetchQuantity:typecompletionHandler:^(NSArray *result,NSError *error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (error)
            {
                //从苹果健康获取步数失败
                NSLog(@"error:%@",error);
                [PersonInfo sharePersonInfo].isAllow = NO;
            }
            else if ([resultcount] == 0)
            {
                //没有步数
                [PersonInfo sharePersonInfo].isAllow = YES;
            }
            // succeeded to retrieve the health data
            else
            {
                [PersonInfo sharePersonInfo].isAllow = YES;
                            //这个函数是去除人为添加的,留下系统记录的步数
                NSArray *array = [selfgetRealHealthData:result];
                
                //统计数组里面的步数
                NSInteger allArrStepCount = 0;
                //一个数组的添加
                for (NSDictionary *dictin array)
                {
                    NSInteger stepCount = [dict[@"stepCount"]integerValue];
                    allArrStepCount = allArrStepCount + stepCount;
                    NSString *dateTime = dict[@"dateTime"];
                    [_dateTimeArr addObject:dateTime];
                }
                
                NSString *value = [NSStringstringWithFormat:@"%ld",(long)allArrStepCount];
                
                [selfsetStepCountAboutDataBase:value locationCount:1getArr:array];
            }
          });
      }];
}


#pragma mark - 从健康中获取真实数据 --去除人为添加的
- (NSArray *)getRealHealthData:(NSArray *)resultArr
{
    NSMutableArray *returnArr = [[NSMutableArrayalloc]init];
    HKQuantityType *stepType = [HKObjectTypequantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
    for (HKQuantitySample *modelin resultArr)
    {
        //HKQuantity 类存储了给定单位的值,此值和单位就构成了数据。和 HKUnit类一样,在使用它时,需要进行实例化。实例化 HKQuantity需要使用到 init(unit:doubleValue:)方法。它可以用来创建一个 quantity(数量)对象。
        HKQuantity *resultQuantity = model.quantity;
        HKUnit *unit = [TTMHealthKitHelperdefaultUnitForQuantityType:stepType];
        double valueDou = [resultQuantity doubleValueForUnit:unit];
        NSString *value = [NSStringstringWithFormat:@"%.0f",valueDou];

        //NSString *startDateStr = [RequestHelper getDateStrFromDate:model.startDate];
        NSString *endDateStr   = [RequestHelpergetDateStrFromDate:model.endDate];
        
        NSDictionary *dict = (NSDictionary *)model.metadata;
        NSInteger wasUserEntered = [dict[@"HKWasUserEntered"]integerValue];
        
        if(wasUserEntered == 1)
        {
           //这是用户自己手动添加的数据
            //NSLog(@"value == %@",value);
        }
        else
        {
    //这是苹果HealthKit记录的数据
            NSDictionary *dictReturn = @{@"dateTime":endDateStr,@"stepCount":value};
            [returnArr addObject:dictReturn];
        }
    }
    return returnArr;
}</span>


相关类下载:http://download.csdn.net/detail/u014220518/9626411