healthkit 之前的计步方案
原文:http://blog.csdn.net/gf771115/article/details/52121489
計(jì)步模塊接觸了一年多,最近又改需求了,所以又換了全新的統(tǒng)計(jì)步數(shù)的方法,整理一下吧。
在iPhone5s以前機(jī)型因?yàn)闆]有陀螺儀的存在,所以需要用加速度傳感器來采集加速度值信息,然后根據(jù)震動(dòng)幅度讓其加入踩點(diǎn)數(shù)組并過濾,獲取自己需要的步數(shù)數(shù)據(jù)。
直接上代碼吧:
首先需要一個(gè)步數(shù)的model如下:
#import <Foundation/Foundation.h>@interface VHSSteps : NSObject //步數(shù)模型 @property(nonatomic,strong) NSDate *date;@property(nonatomic,assign) int record_no;@property(nonatomic, strong) NSString *record_time;@property(nonatomic,assign) int step;//g是一個(gè)震動(dòng)幅度的系數(shù),通過一定的判斷條件來判斷是否計(jì)做一步 @property(nonatomic,assign) double g;@end然后是如何獲取步數(shù),首先判斷傳感器是否可用
//加速度傳感器self.motionManager = [[CMMotionManager alloc] init];// 檢查傳感器到底在設(shè)備上是否可用if (!self.motionManager.accelerometerAvailable) {return;} else {// 更新頻率是100Hz//以pull方式獲取數(shù)據(jù)self.motionManager.accelerometerUpdateInterval = 1.0/40;}可用的話開始實(shí)現(xiàn)統(tǒng)計(jì)步數(shù)的算法
@try{//如果不支持陀螺儀,需要用加速傳感器來采集數(shù)據(jù)if (!self.motionManager.isAccelerometerActive) {// isAccelerometerAvailable方法用來查看加速度器的狀態(tài):是否Active(啟動(dòng))。// 加速度傳感器采集的原始數(shù)組if (arrAll == nil) {arrAll = [[NSMutableArray alloc] init];}else {[arrAll removeAllObjects];}/*1.push方式這種方式,是實(shí)時(shí)獲取到Accelerometer的數(shù)據(jù),并且用相應(yīng)的隊(duì)列來顯示。即主動(dòng)獲取加速計(jì)的數(shù)據(jù)。*/NSOperationQueue *queue = [[NSOperationQueue alloc] init];[self.motionManager startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData *accelerometerData, NSError *error){if (!self.motionManager.isAccelerometerActive) {return;}//三個(gè)方向加速度值double x = accelerometerData.acceleration.x;double y = accelerometerData.acceleration.y;double z = accelerometerData.acceleration.z;//g是一個(gè)double值 ,根據(jù)它的大小來判斷是否計(jì)為1步.double g = sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2)) - 1;//將信息保存在步數(shù)模型中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;// 加速度傳感器采集的原始數(shù)組 [arrAll addObject:stepsAll];// 每采集10條,大約1.2秒的數(shù)據(jù)時(shí),進(jìn)行分析if (arrAll.count == 10) {// 步數(shù)緩存數(shù)組NSMutableArray *arrBuffer = [[NSMutableArray alloc] init];arrBuffer = [arrAll copy];[arrAll removeAllObjects];// 踩點(diǎn)數(shù)組NSMutableArray *arrCaiDian = [[NSMutableArray alloc] init];//遍歷步數(shù)緩存數(shù)組for (int i = 1; i < arrBuffer.count - 2; i++) {//如果數(shù)組個(gè)數(shù)大于3,繼續(xù),否則跳出循環(huán),用連續(xù)的三個(gè)點(diǎn),要判斷其振幅是否一樣,如果一樣,然并卵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];//控制震動(dòng)幅度,,,,,,根據(jù)震動(dòng)幅度讓其加入踩點(diǎn)數(shù)組,if (bufferCurrent.g < -0.12 && bufferCurrent.g < bufferPrevious.g && bufferCurrent.g < bufferNext.g) {[arrCaiDian addObject:bufferCurrent];}}//如果沒有步數(shù)數(shù)組,初始化if (nil == self.arrSteps) {self.arrSteps = [[NSMutableArray alloc] init];self.arrStepsSave = [[NSMutableArray alloc] init];}// 踩點(diǎn)過濾for (int j = 0; j < arrCaiDian.count; j++) {VHSSteps *caidianCurrent = (VHSSteps *)[arrCaiDian objectAtIndex:j];//如果之前的步數(shù)為0,則重新開始記錄if (self.arrSteps.count == 0) {//上次記錄的時(shí)間lastDate = caidianCurrent.date;// 重新開始時(shí),紀(jì)錄No初始化record_no = 1;record_no_save = 1;// 運(yùn)動(dòng)識(shí)別號(hào)NSTimeInterval interval = [caidianCurrent.date timeIntervalSince1970];NSNumber *numInter = [[NSNumber alloc] initWithDouble:interval*1000];long long llInter = numInter.longLongValue;//運(yùn)動(dòng)識(shí)別idself.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步,超過此范圍,數(shù)據(jù)有可能丟失int min = 259;if (intervalCaidian >= min) {if (self.motionManager.isAccelerometerActive) {//存一下時(shí)間lastDate = caidianCurrent.date;if (intervalCaidian >= ACCELERO_START_TIME * 1000) {// 計(jì)步器開始計(jì)步時(shí)間(秒)self.startStep = 0;}if (self.startStep < ACCELERO_START_STEP) {//計(jì)步器開始計(jì)步步數(shù) (步) self.startStep ++;break;}else if (self.startStep == ACCELERO_START_STEP) {self.startStep ++;// 計(jì)步器開始步數(shù)// 運(yùn)動(dòng)步數(shù)(總計(jì))self.step = self.step + self.startStep;}else {self.step ++;}//步數(shù)在這里NSLog(@"步數(shù)%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步保存一條數(shù)據(jù)(將來插入DB用)VHSSteps *arrStepsSaveVHSSteps = (VHSSteps *)[self.arrStepsSave lastObject];int intervalStep = caidianCurrent.step - arrStepsSaveVHSSteps.step;// DB_STEP_INTERVAL 數(shù)據(jù)庫(kù)存儲(chǔ)步數(shù)采集間隔(步) 100步if (self.arrStepsSave.count == 1 || intervalStep >= DB_STEP_INTERVAL) {//保存次數(shù)record_no_save++;caidianCurrent.record_no = record_no_save;[self.arrStepsSave addObject:caidianCurrent];// 備份當(dāng)前運(yùn)動(dòng)數(shù)據(jù)至文件中,以備APP異常退出時(shí)數(shù)據(jù)也不會(huì)丟失// [self bkRunningData]; }}}// 運(yùn)動(dòng)提醒檢查// [self checkActionAlarm]; }}}}];}}@catch (NSException * e) {NSLog(@"Exception: %@", e);return;}然后iPhone 5s出現(xiàn)了, 增加了 M7 運(yùn)動(dòng)協(xié)處理器,也帶來了CMStepCounter類,從此我們就不用自己計(jì)算步數(shù)了,只要直接讀取就好。
首先還是要檢測(cè)協(xié)處理器是否可用
if (!([CMStepCounter isStepCountingAvailable] || [CMMotionActivityManager isActivityAvailable])) {NSString *msg = @"demo只支持iPhone5s以上機(jī)型.";UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Opps!"message:msgdelegate:nilcancelButtonTitle:@"OK"otherButtonTitles:nil];[alert show];}然后才是獲取步數(shù)的方法,主要有兩種:
計(jì)步 第一種方法
??? ?
???? startStepCountingUpdatesToQueue:updateOn:withHandler:
??? ?
???? 開始分發(fā)當(dāng)前步數(shù)計(jì)數(shù)數(shù)據(jù)到第三方應(yīng)用
??? ?
???? - (void)startStepCountingUpdatesToQueue:(NSOperationQueue *)queue updateOn:(NSInteger)stepCounts withHandler:(CMStepUpdateHandler)handler
???? Parameters
??? ?
???? queue
??? ?
???? 被指定執(zhí)行特定的handler塊的操作隊(duì)列。第三方可以指定一個(gè)定制隊(duì)列或者使用操作隊(duì)列協(xié)助app的主線程。該參數(shù)不能為nil
??? ?
???? stepCounts
??? ?
???? 記錄的步伐數(shù)據(jù),達(dá)到該數(shù)值去執(zhí)行handler塊。該數(shù)值必須大于0
??? ?
???? handler
??? ?
???? 該塊在步伐計(jì)數(shù)達(dá)到或超出數(shù)值時(shí)會(huì)被執(zhí)行,該參數(shù)不能為nil。更多塊方法信息參考CMStepQueryHandler。
??? ?
???? Discussion
??? ?
???? 該方法實(shí)現(xiàn)對(duì)用戶步伐數(shù)據(jù)的追蹤,并周期性地喚起塊方法去分發(fā)結(jié)果。當(dāng)?shù)谌秸{(diào)用了該方法,步伐計(jì)數(shù)器會(huì)重置當(dāng)前步伐數(shù)為0,并開始計(jì)數(shù)。每次計(jì)數(shù)到達(dá)指定的步伐數(shù)時(shí),會(huì)執(zhí)行指定的handler塊方法。比如,當(dāng)設(shè)定stepCounts為100時(shí),會(huì)在100,200,300等數(shù)目時(shí)發(fā)送更新,激活該塊方法。每次發(fā)送到該塊方法的步伐數(shù)目都是從你調(diào)用該方法開始的步伐數(shù)目總和。
??? ?
???? 每次超過設(shè)定步數(shù)值時(shí),指定的處理程序塊handler會(huì)被執(zhí)行。如果當(dāng)超過設(shè)定值時(shí)第三方應(yīng)用處在被掛起的狀態(tài),那程序塊也不會(huì)被執(zhí)行。當(dāng)?shù)谌綉?yīng)用被喚醒,程序塊也不會(huì)執(zhí)行,直到再次超過設(shè)定步數(shù)值。
??? ?
???? 可以調(diào)用stopStepCountingUpdates方法去停止分發(fā)步數(shù)計(jì)數(shù),當(dāng)然當(dāng)步數(shù)計(jì)數(shù)對(duì)像被銷毀的時(shí)候,分發(fā)過程也會(huì)被停止。
????
代碼如下:
?
計(jì)步 第二種方法
????
???? queryStepCountStartingFrom:to:toQueue:withHandler:
??? ?
???? 收集并返回某一時(shí)間段內(nèi)的歷史步數(shù)數(shù)據(jù)
??? ?
???? - (void)queryStepCountStartingFrom:(NSDate *)start to:(NSDate *)end toQueue:(NSOperationQueue *)queuewithHandler:(CMStepQueryHandler)handler
???? Parameters
??? ?
???? start
??? ?
???? 收集步數(shù)數(shù)據(jù)的開始時(shí)間,該參數(shù)不能為 nil.
??? ?
???? end
??? ?
???? 收集步數(shù)數(shù)據(jù)的停止時(shí)間,該參數(shù)不能為nil.
??? ?
???? queue
??? ?
???? 執(zhí)行指定handler塊的操作隊(duì)列,第三方可以指定一個(gè)定制隊(duì)列或者使用操作隊(duì)列協(xié)助app的主線程。該參數(shù)不能為nil
??? ?
???? handler
??? ?
???? 執(zhí)行處理結(jié)果的塊方法,該參數(shù)不能為nil。更多塊方法信息參考CMStepQueryHandler。
??? ?
???? Discussion
??? ?
???? 該方法為異步方法,會(huì)立即返回并且把結(jié)果分發(fā)到指定的handler塊中處理。系統(tǒng)最多僅存儲(chǔ)最近7天內(nèi)的有效步數(shù)數(shù)據(jù)。如果在指定時(shí)間范圍內(nèi)沒有數(shù)據(jù),則會(huì)傳遞一個(gè)0值到handler塊中。
代碼如下
另外,iOS7還增加了CMMotionActivity類,用來獲取運(yùn)動(dòng)狀態(tài)
if ([CMMotionActivityManager isActivityAvailable]) {self.activityManager = [[CMMotionActivityManager alloc] init];[self.activityManager startActivityUpdatesToQueue:self.operationQueuewithHandler:^(CMMotionActivity *activity) {dispatch_async(dispatch_get_main_queue(), ^{NSString *status = [weakSelf statusForActivity:activity];NSString *confidence = [weakSelf stringFromConfidence:activity.confidence];weakSelf.statusLabel.text = [NSString stringWithFormat:@"狀態(tài): %@", 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;} }好吧,隨著時(shí)間的推移,iOS8來了,也帶來了healthkit,不過之前的方法滿足需求也就還是用的CMStepCounter方法。
不過最近客戶改需求了,手環(huán),iWatch的數(shù)據(jù)也需要統(tǒng)計(jì)進(jìn)來,就不得不用healthkit的方法了。
?還是老套路,先檢查能不能用
//查看healthKit在設(shè)備上是否可用,ipad不支持HealthKitif(![HKHealthStore isHealthDataAvailable]){NSLog(@"設(shè)備不支持healthKit");}然后獲取步數(shù)
//創(chuàng)建healthStore實(shí)例對(duì)象self.healthStore = [[HKHealthStore alloc] init];//設(shè)置需要獲取的權(quán)限這里僅設(shè)置了步數(shù)HKObjectType *stepCount = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];NSSet *healthSet = [NSSet setWithObjects:stepCount, nil];//從健康應(yīng)用中獲取權(quán)限[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]; //定義需要獲取的數(shù)據(jù)為步數(shù)HKQuantityType *quantityType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount]; //設(shè)置獲取的步數(shù)時(shí)間間隔NSDateComponents *dateComponents = [[NSDateComponents alloc] init];dateComponents.day = 1;NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:today endDate:next options:HKQueryOptionStrictStartDate];//創(chuàng)建查詢統(tǒng)計(jì)對(duì)象collectionQueryHKStatisticsCollectionQuery *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對(duì)象中的name可用于區(qū)分健康數(shù)據(jù)來源if ([source.name isEqualToString:deviceName]) {float steps = [[statistic sumQuantityForSource:source] doubleValueForUnit:[HKUnit countUnit]];numberOfSteps += steps;} //deviceName是根據(jù)接入的設(shè)備做的標(biāo)記,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:@"小米運(yùn)動(dòng)"] || [source.bundleIdentifier isEqualToString:@"HM.wristband"]) {float steps = [[statistic sumQuantityForSource:source] doubleValueForUnit:[HKUnit countUnit]];numberOfSteps += steps;}}}}NSLog(@"ff = %f",numberOfSteps);//步數(shù)看這里就好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(@"獲取步數(shù)權(quán)限失敗");}}];demo完整代碼在這里:
加速度傳感器進(jìn)行計(jì)步
CMStepCounter獲取健康步數(shù)
總結(jié)
以上是生活随笔為你收集整理的healthkit 之前的计步方案的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: springmvc 切面记录操作日志
- 下一篇: 图灵奖得主Judea Pearl:最近值