计步器的实现方式主要有那么两种
1.通过直接调用系统的健康数据,基于HealthKit
框架的,但是貌似是一小时更新一次数据。如果要实时获取步数,这种方式并不是最佳。
2.基于CoreMotion框架,顾名思义就是加速计/加速度传感器
》最早出现在iOS设备上的传感器之一
》加速计用于检测设备在X、Y、Z轴上的加速度 (哪个方向有力的作用)
》加速计可以用于检测设备的摇晃,经典应用场景(例如摇一摇、计步器等)
源码:
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface StepManager : NSObject @property (nonatomic) NSInteger step; // 运动步数(总计) + (StepManager *)sharedManager; //开始计步 - (void)startWithStep; //得到计步所消耗的卡路里 //+ (NSInteger)getStepCalorie; // //得到所走的路程(单位:米) //+ (CGFloat)getStepDistance; // //得到运动所用的时间 //+ (NSInteger)getStepTime; @end
#import "StepManager.h" #import "StepModel.h" #import <CoreMotion/CoreMotion.h> // 计步器开始计步时间(秒) #define ACCELERO_START_TIME 2 // 计步器开始计步步数(步) #define ACCELERO_START_STEP 100 // 数据库存储步数采集间隔(步) #define DB_STEP_INTERVAL 1 @interface StepManager () { NSMutableArray *arrAll; // 加速度传感器采集的原始数组 int record_no_save; int record_no; NSDate *lastDate; } @property (nonatomic) NSInteger startStep; // 计步器开始步数 @property (nonatomic, retain) NSMutableArray *arrSteps; // 步数数组 @property (nonatomic, retain) NSMutableArray *arrStepsSave; // 数据库纪录步数数组 @property (nonatomic) CGFloat gpsDistance; // GPS轨迹的移动距离(总计) @property (nonatomic) CGFloat agoGpsDistance; // GPS轨迹的移动距离(之前) @property (nonatomic) CGFloat agoActionDistance; // 实际运动的移动距离(之前) @property (nonatomic, retain) NSString *actionId; // 运动识别ID @property (nonatomic) CGFloat distance; // 运动里程(总计) @property (nonatomic) NSInteger calorie; // 消耗卡路里(总计) @property (nonatomic) NSInteger second; // 运动用时(总计) @end @implementation StepManager static StepManager *sharedManager; static CMMotionManager *motionManager; + (StepManager *)sharedManager { @synchronized (self) { if (!sharedManager) { sharedManager = [[StepManager alloc]init]; motionManager = [[CMMotionManager alloc]init]; } } return sharedManager; } //开始计步 - (void)startWithStep { if (!motionManager.isAccelerometerAvailable) { NSLog(@"加速度传感器不可用"); return; }else { motionManager.accelerometerUpdateInterval = 1.0/40; } [self startAccelerometer]; } - (void)startAccelerometer { /* @try 。。。@catch 。。。 * @try 后面跟或许会出现异常的程序,代码块 * @catch 当@try抛出异常时 系统会进行异常捕捉 具体可以了解下 NSException 异常类 @catch 在这里处理 程序所抛出的异常 */ @try { //如果不支持陀螺仪,需要用加速传感器来采集数据 if (!motionManager.isAccelerometerActive) {// isAccelerometerAvailable方法用来查看加速度器的状态:是否Active(启动)。 // 加速度传感器采集的原始数组 if (arrAll == nil) { arrAll = [[NSMutableArray alloc] init]; } else { [arrAll removeAllObjects]; } /* 1.push方式 这种方式,是实时获取到Accelerometer的数据,并且用相应的队列来显示。即主动获取加速计的数据。 */ NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [motionManager startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData *accelerometerData, NSError *error){ if (!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; //将信息保存在步数模型中 StepModel *stepsAll = [[StepModel 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; } StepModel *bufferPrevious = (StepModel *)[arrBuffer objectAtIndex:i-1]; StepModel *bufferCurrent = (StepModel *)[arrBuffer objectAtIndex:i]; StepModel *bufferNext = (StepModel *)[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++) { StepModel *caidianCurrent = (StepModel *)[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 = (int)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 (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(@"步数%ld",self.step); int intervalMillSecond = [caidianCurrent.date timeIntervalSinceDate:[[self.arrSteps lastObject] date]] * 1000; if (intervalMillSecond >= 1000) { record_no++; caidianCurrent.record_no = record_no; caidianCurrent.step = (int)self.step; [self.arrSteps addObject:caidianCurrent]; } // 每隔100步保存一条数据(将来插入DB用) StepModel *arrStepsSaveVHSSteps = (StepModel *)[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]; NSLog(@"---***%ld",self.step); // 备份当前运动数据至文件中,以备APP异常退出时数据也不会丢失 // [self bkRunningData]; } } } // 运动提醒检查 // [self checkActionAlarm]; } } } }]; } }@catch (NSException * e) { NSLog(@"Exception: %@", e); return; } } ////得到计步所消耗的卡路里 //+ (NSInteger)getStepCalorie //{ // 在这里原谅我并没有对其实现。本以为卡路里和步数的换算单位,一个公式就可以了。不查不知道,一查吓一跳原来还和其他众多因素有关:走路的快慢,步子的大小,体重的大小等等有关。。。笔者已吓尿,还是找算法大牛吧。 //} // ////得到所走的路程(单位:米) //+ (CGFloat)getStepDistance //{ // //} // ////得到运动所用的时间 //+ (NSInteger)getStepTime //{ // //}
外部使用方式:
#import "ViewController.h" #import "StepManager.h" @interface ViewController () { NSTimer *_timer; UILabel *lable; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [[StepManager sharedManager] startWithStep]; lable =[[ UILabel alloc]initWithFrame:CGRectMake(100, 300, 300, 40)]; [self.view addSubview:lable]; _timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(getStepNumber) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode]; } - (void)getStepNumber { lable.text = [NSString stringWithFormat:@"我走了 %ld步",[StepManager sharedManager].step]; }
当然还有特别注意,应用程序在后台也要做采集动作:
- (void)applicationDidEnterBackground:(UIApplication *)application { //播放一段无声音乐,使其可以一直在后台进行计步 此方法为第三方 若要详细了解,请下载demo自行研究 [[MMPDeepSleepPreventer sharedSingleton] startPreventSleep]; }