又改需求了,所以又换了全新的统计步数的方法,整理一下吧。

在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/;
}

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

 @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, ) + pow(y, ) + pow(z, )) - ; //将信息保存在步数模型中
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 == ) { // 步数缓存数组
NSMutableArray *arrBuffer = [[NSMutableArray alloc] init]; arrBuffer = [arrAll copy];
[arrAll removeAllObjects]; // 踩点数组
NSMutableArray *arrCaiDian = [[NSMutableArray alloc] init]; //遍历步数缓存数组
for (int i = ; i < arrBuffer.count - ; i++) {
//如果数组个数大于3,继续,否则跳出循环,用连续的三个点,要判断其振幅是否一样,如果一样,然并卵
if (![arrBuffer objectAtIndex:i-] || ![arrBuffer objectAtIndex:i] || ![arrBuffer objectAtIndex:i+])
{
continue;
}
VHSSteps *bufferPrevious = (VHSSteps *)[arrBuffer objectAtIndex:i-];
VHSSteps *bufferCurrent = (VHSSteps *)[arrBuffer objectAtIndex:i];
VHSSteps *bufferNext = (VHSSteps *)[arrBuffer objectAtIndex:i+];
//控制震动幅度,,,,,,根据震动幅度让其加入踩点数组,
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 = ; j < arrCaiDian.count; j++) {
VHSSteps *caidianCurrent = (VHSSteps *)[arrCaiDian objectAtIndex:j]; //如果之前的步数为0,则重新开始记录
if (self.arrSteps.count == ) {
//上次记录的时间
lastDate = caidianCurrent.date; // 重新开始时,纪录No初始化
record_no = ;
record_no_save = ; // 运动识别号
NSTimeInterval interval = [caidianCurrent.date timeIntervalSince1970];
NSNumber *numInter = [[NSNumber alloc] initWithDouble:interval*];
long long llInter = numInter.longLongValue;
//运动识别id
self.actionId = [NSString stringWithFormat:@"%lld",llInter]; self.distance = 0.00f;
self.second = ;
self.calorie = ;
self.step = ; 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] * ; // 步行最大每秒2.5步,跑步最大每秒3.5步,超过此范围,数据有可能丢失
int min = ;
if (intervalCaidian >= min) { if (self.motionManager.isAccelerometerActive) { //存一下时间
lastDate = caidianCurrent.date; if (intervalCaidian >= ACCELERO_START_TIME * ) {// 计步器开始计步时间(秒)
self.startStep = ;
} 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]] * ;
if (intervalMillSecond >= ) { 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 == || intervalStep >= DB_STEP_INTERVAL) {
//保存次数
record_no_save++;
caidianCurrent.record_no = record_no_save;
[self.arrStepsSave addObject:caidianCurrent];
}
}
}
}
}
}
}]; }
}@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:
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: toDate:startDate options:]; 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知识普及点这里

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

   //查看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:**];
       //定义需要获取的数据为步数
HKQuantityType *quantityType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
       //设置获取的步数时间间隔
       NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
dateComponents.day = ; NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:today endDate:next options:HKQueryOptionStrictStartDate];
//创建查询统计对象collectionQuery
       HKStatisticsCollectionQuery *collectionQuery = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:quantityType quantitySamplePredicate:predicate options: HKStatisticsOptionCumulativeSum | HKStatisticsOptionSeparateBySource anchorDate:[NSDate dateWithTimeIntervalSince1970:] intervalComponents:dateComponents];
collectionQuery.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection * __nullable result, NSError * __nullable error) {
float numberOfSteps = ;
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];
};
[self.healthStore executeQuery:collectionQuery];
}
else
{
NSLog(@"获取步数权限失败");
}
}];

demo完整代码在这里:

加速度传感器进行计步

CMStepCounter获取健康步数

healthkit获取步数

iOS获取健康步数从加速计到healthkit的更多相关文章

  1. iOS获取设备唯一标识的8种方法

    8种iOS获取设备唯一标识的方法,希望对大家有用. UDID UDID(Unique Device Identifier),iOS 设备的唯一识别码,是一个40位十六进制序列(越狱的设备通过某些工具可 ...

  2. iOS 获取文件的目录路径的几种方法 [转]

    iOS 获取文件的目录路径的几种方法 2 years ago davidzhang iphone沙箱模型的有四个文件夹,分别是什么,永久数据存储一般放在什么位置,得到模拟器的路径的简单方式是什么. d ...

  3. iOS获取设备型号、装置类型等信息

    iOS获取设备型号.设备类型等信息 设备标识 关于设备标识,历史上盛行过很多英雄,比如UDID.Mac地址.OpenUDID等,然而他们都陆陆续续倒在了苹果的门下.苹果目前提供了2个方法供App获取设 ...

  4. Swift3.0 iOS获取当前时间 - 年月日时分秒星期

    Swift3.0 iOS获取当前时间 - 年月日时分秒星期func getTimes() -> [Int] { var timers: [Int] = [] // 返回的数组 let calen ...

  5. IOS 获取最新设备型号方法

    1.IOS 获取最新设备型号方法列表最新对照表:http://theiphonewiki.com/wiki/Models方法: #import "sys/utsname.h” struct ...

  6. ios 获取通讯录的所有信息

    iOS获取通讯录全部信息 ABAddressBookRef addressBook = ABAddressBookCreate(); CFArrayRef results = ABAddressBoo ...

  7. iOS获取汉字的拼音

    在iOS开发中经常涉及到汉字的排序,最常见的就是需要根据首字母的字符顺序排列,比如常见的通讯录等.总结出来,大致可以分为两种方法,其中参考文献[1]中提供的方法十分复杂,利用查表的方法是先,并且代码量 ...

  8. IOS获取物理尺寸中7Plus中获取的是7的物理尺寸

    IOS获取物理尺寸中7Plus中获取的是7的物理尺寸: 在开发调试过程中我的7Plus手机获取[uiscreen mainscreen].bounds为750  .1334. 解决方案:在手机中的显示 ...

  9. iOS: 获取文件路径

    iOS: 获取文件路径   // 例如 - (NSString *)applicationDocumentsDirectory { return [NSSearchPathForDirectories ...

随机推荐

  1. 什么是image crop?

    一直对image crop很困惑,总算是看到了一篇描述较为简洁的说明:图像crop就是指从图像中移除不需要的信息,只保留需要的部分

  2. jQuery实现Twitter的自动文字补齐特效

    上图效果可以使用jQuery插件Typeahead.js来实现,这款jQuery插件来自于Twitter的一个新的项目,支持远程和本地的数据集.比较有特色的地方在于你可以将数据集使用本地存储(loca ...

  3. importExcel运用注解实现EXCEL导入poi类

    JAVA报表 package com.app.common.excel; import java.io.File; import java.io.FileInputStream; import jav ...

  4. 【UVA1633】禁止的回文串(状压DP)

    题意: 输入正整数n和k(1<=n<=400,1<=k<=10),求长度为n的01串中有多少个不含长度至少为k的回文连续子串.例如,n=k=3时只有4个串满足条件:001,01 ...

  5. 在eclipse中安装activiti插件

    最近在学习activiti,先学习安装插件吧. 单击help->Install new Software 然后添加资源 name:activiti location:http://activit ...

  6. 14.8.4 Moving or Copying InnoDB Tables to Another Machine 移动或者拷贝 InnoDB 表到另外机器

    14.8.4 Moving or Copying InnoDB Tables to Another Machine 移动或者拷贝 InnoDB 表到另外机器 这个章节描述技术关于移动或者复制一些或者所 ...

  7. 【HDOJ】1829 A Bug's Life

    并查集变型.题意就是x与y是互斥的,下列是否数据是否可保证x-y是否均为互斥. #include <cstdio> #include <cstring> #define MAX ...

  8. This configuration file was broken by system-config-keyboard

    posts • Page of problem with startx Postby evarie » // :: Normally i can get started with the x wind ...

  9. QQ游戏百万人同时在线服务器架构实现

    转载自:http://morton5555.blog.163.com/blog/static/976407162012013112545710/# QQ游戏于前几日终于突破了百万人同时在线的关口,向着 ...

  10. bzoj2743

    其实和bzoj1878类似只不过要求的是区间内数量多于1个的数字种数其实还是按照bzoj1878做只不过我们是把每一种数字下一个出现的位置+1,并把这个位置置为0 ..] of longint; ma ...