当前位置: 首页 > 编程笔记 >

详解iOS 计步器的几种实现方式

申屠飞
2023-03-14
本文向大家介绍详解iOS 计步器的几种实现方式,包括了详解iOS 计步器的几种实现方式的使用技巧和注意事项,需要的朋友参考一下

这篇文章介绍两种可以获取计步数据的方法,一种是采用CMPedometer获取手机计步器数据,另一种是采用HealthKit框架从手机健康App中获取计步数据。另外玩了一下写入数据到健康App。有描述不当之处,望指点。

花絮(用HealthKit框架构建app,写入数据到苹果健康app中,QQ和Keep等第三方app的运动数据都会随之改变,猜测它们的运动数据是直接从苹果健康app中获取,而且没有过滤掉其它数据来源。而微信运动的数据不会变,猜测其来源可能是使用CMPedometer类获取的,因为测试发现把微信运动的数据来源(苹果健康)关闭后,依然会有运动数据,而且该运动数据和CMPedometer类获取的一致。)

使用CMPedometer类来获取步数和距离

使用时需要导入<CoreMotion/CoreMotion.h>,此类在iOS8之后才可用,在iOS8之前,使用CMStepCounter类(在iOS8之后被CMPedometer替代)来获取步数,使用方法如CMPedometer类相似。

CMPedometer

+ (BOOL)isStepCountingAvailable; 设备是否支持计步功能

+ (BOOL)isDistanceAvailable; 除了计步,设备是否支持距离估计

+ (BOOL)isFloorCountingAvailable; 除了计步,设备是否支持台阶计数

+ (BOOL)isPaceAvailable NS_AVAILABLE(NA,9_0);除了计步,设备是否支持速度估计

+(BOOL)isCadenceAvailable NS_AVAILABLE(NA,9_0);除了计步,设备是否支持节奏估计

+ (BOOL)isPedometerEventTrackingAvailable NS_AVAILABLE(NA,10_0) __WATCHOS_AVAILABLE(3_0);设备是否支持计步器事件

- (void)queryPedometerDataFromDate:(NSDate *)start toDate:(NSDate *)end withHandler:(CMPedometerHandler)handler;在给定时间范围内查询用户的行走活动,数据最多可以使用7天内有效,返回的数据是从系统范围的历史记录中计算出来的,该历史记录是在后台连续收集的。结果返回在串行队列中。

- (void)startPedometerUpdatesFromDate:(NSDate *)start withHandler:(CMPedometerHandler)handler;在串行队列上启动一系列连续计步器更新到处理程序。 对于每次更新,应用程序将从指定的开始日期和与最新确定相关联的时间戳开始收到累积的行人活动。 如果应用程序在后台进行背景调整,则应用程序将在下次更新中收到在后台期间累积的所有行人活动。

-(void)stopPedometerUpdates;停止计步器更新

-(void)startPedometerEventUpdatesWithHandler:(CMPedometerEventHandler)handler NS_AVAILABLE(NA,10_0) __WATCHOS_AVAILABLE(3_0);在串行队列上启动计步器事件更新。 事件仅在应用程序在前台/后台运行时可用。

-(void)stopPedometerEventUpdates NS_AVAILABLE(NA,10_0) __WATCHOS_AVAILABLE(3_0);停止计步器事件更新

CMPedometerData

@property(readonly, nonatomic) NSDate *startDate;计步器数据有效期间的开始时间。这是会话或历史查询请求的开始时间。

@property(readonly, nonatomic) NSDate *endDate;计步器数据有效期间的结束时间。对于更新,这是最新更新的时间。 对于历史查询,这是请求的结束时间。

@property(readonly, nonatomic) NSNumber *numberOfSteps;用户的步数

@property(readonly, nonatomic, nullable) NSNumber *distance; 用户行走和跑步时估计的一米为单位的距离。若设备不支持则值为nil

@property(readonly, nonatomic, nullable) NSNumber *floorsAscended;上楼的大概楼层数,若设备不支持则值为nil

@property(readonly, nonatomic, nullable) NSNumber *floorsDescended;下楼的大概楼层数, 若设备不支持则值为nil

@property(readonly, nonatomic, nullable) NSNumber *currentPace NS_AVAILABLE(NA,9_0);对于更新,这将以s / m(每米秒)返回当前速度。 如果满足以下条件,则值为零:1. 资料尚未公布 2. 历史查询 3.平台不支持

@property(readonly, nonatomic, nullable) NSNumber *currentCadence NS_AVAILABLE(NA,9_0);对于更新,这将返回以秒为单位执行行走的节奏。 如果满足以下条件,则值为零:1. 资料尚未公布 2. 历史查询 3.平台不支持

@property(readonly, nonatomic, nullable) NSNumber *averageActivePace NS_AVAILABLE(NA,10_0);对于更新,这将返回自startPedometerUpdatesFromDate:withHandler :,以s / m(每米秒))的平均活动速度。 对于历史查询,这将返回startDate和endDate之间的平均活动速度。 平均主动速度省略了非活动时间,平均步调从用户移动。 如果满足以下条件,则值为零:1. 对于历史信息查询,信息无效。例如用户在开始时间和结束时间内没有移动 2. 平台不支持

CMPedometerEvent

@property(readonly, nonatomic) NSDate *date;事件发生的时间

@property(readonly, nonatomic) CMPedometerEventType type;描述行走活动过渡的事件类型

typedef void (^CMPedometerHandler)(CMPedometerData * __nullable pedometerData, NSError * __nullable error) __TVOS_PROHIBITED;当计步器数据可用时要调用的block的类型

typedef void (^CMPedometerEventHandler)(CMPedometerEvent * __nullable pedometerEvent, NSError * __nullable error) NS_AVAILABLE(NA, 10_0) __WATCHOS_AVAILABLE(3_0) __TVOS_PROHIBITED;//当计步器事件可用时将被调用的block的类型。

获取步数和距离的方法

1、使用<CoreMotion/CoreMotion.h>库需要在info.plist文件中增加NSMotionUsageDescription键。

2、可以使用isStepCountingAvailable或者isDistanceAvailable来检查设备是否支持计步功能或距离功能。

3、创建CMPedometer实例对象

 /// 创建计步器对象
 if ([CMPedometer isStepCountingAvailable]) { // 8.0 之后可使用
   self.pedometer = [[CMPedometer alloc] init];
 }

4、调用- (void)startPedometerUpdatesFromDate:(NSDate *)start withHandler:(CMPedometerHandler)handler方法获取从某个时间点到现在的步数,距离,楼层等信息。此方法会实时更新数据。

 [self.pedometer startPedometerUpdatesFromDate:fromDate withHandler:^(CMPedometerData * _Nullable pedometerData, NSError * _Nullable error) {
  // 如果没有错误,具体信息从pedometerData参数中获取
 }];

5、不需要使用的时候,调用stopPedometerUpdates方法停止更新

 [self.pedometer stopPedometerUpdates];

6、如果不需要实时更新数据,可直接调用- (void)queryPedometerDataFromDate:(NSDate *)start toDate:(NSDate *)end withHandler:(CMPedometerHandler)handler;查询某个时间段内的数据,不过只能查询七天内的数据。

 [self.pedometer queryPedometerDataFromDate:start toDate:end withHandler:^(CMPedometerData * _Nullable pedometerData, NSError * _Nullable error) {
  // 如果没有错误,具体信息从pedometerData参数中获取
 }];

使用HealthKit框架获取苹果健康数据

在HealthKit中,使用HKHealthStore类来访问健康数据,健康数据的类型有很多类,苹果健康app中的健身记录、营养摄入、睡眠状况等等都可以进行数据读取和共享(即第三方app写入数据到苹果健康app)。

大概步骤:

1、在Xcode中, 打开HealthKit 功能

开启HealthKit功能

1、调用isHealthDataAvailable方法检查设备HealthKit是否可用。

 if ([HKHealthStore isHealthDataAvailable]) {
 // add code to use HealthKit here...
 }

2、如果可用,创建HKHealthStore对象

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

3、向用户请求授权共享或读取健康数据, 调用- (void)requestAuthorizationToShareTypes:(nullable NSSet<HKSampleType *> *)typesToShare readTypes:(nullable NSSet<HKObjectType *> *)typesToRead completion:(void (^)(BOOL success, NSError * _Nullable error))completion;方法,例如下面请求读取步数和距离数据

 NSSet<HKSampleType *> *shareTypes = nil;
 HKQuantityType *stepType = [HKQuantityType quantityTypeForIdentifier:(HKQuantityTypeIdentifierStepCount)];
 HKQuantityType *distanceType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];
 NSSet<HKObjectType *> *readTypes = [NSSet setWithObjects:stepType, distanceType, nil];
 [self.healthStore requestAuthorizationToShareTypes:shareTypes readTypes:readTypes completion:^(BOOL success, NSError * _Nullable error) {
    
 }];

4、在info.plist文件中,增加NSHealthShareUsageDescription用于读取数据的描述和NSHealthUpdateUsageDescription用于写入数据的描述

5、用户授权之后,就可以对健康数据中授权的项目进行读取或写入操作。下面的代码是查询一段历史的计步记录的示例,如CMPedemoter不同的是查询到的数据不是实时更新的。

   // 查询数据的类型,比如计步,行走+跑步距离等等 
   HKQuantityType *quantityType = [HKQuantityType quantityTypeForIdentifier:(HKQuantityTypeIdentifierStepCount)];
   // 谓词,用于限制查询返回结果
   NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:start endDate:end options:(HKQueryOptionNone)];

   NSCalendar *calendar = [NSCalendar currentCalendar];
   NSDateComponents *anchorComponents = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:[NSDate date]];
   // 用于锚集合的时间间隔
   NSDate *anchorDate = [calendar dateFromComponents:anchorComponents];

   // 采样时间间隔
   NSDateComponents *intervalComponents = [[NSDateComponents alloc] init];
   intervalComponents.day = 1;
   
   // 创建统计查询对象
   HKStatisticsCollectionQuery *query = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:quantityType quantitySamplePredicate:predicate options:(HKStatisticsOptionCumulativeSum|HKStatisticsOptionSeparateBySource) anchorDate:anchorDate intervalComponents:intervalComponents];
   query.initialResultsHandler = ^(HKStatisticsCollectionQuery * _Nonnull query, HKStatisticsCollection * _Nullable result, NSError * _Nullable error) {
     NSMutableArray *resultArr = [NSMutableArray array];
     if (error) {
       NSLog(@"error: %@", error);
     } else {
     for (HKStatistics *statistics in [result statistics]) {
       NSLog(@"statics: %@,\n sources: %@", statistics, statistics.sources);
       for (HKSource *source in statistics.sources) {
         // 过滤掉其它应用写入的健康数据
         if ([source.name isEqualToString:[UIDevice currentDevice].name]) {
           // 获取到步数
           double step = round([[statistics sumQuantityForSource:source] doubleValueForUnit:[HKUnit countUnit]]); 
         }
       }
     }
   }
   // 执行查询请求
   [self.healthStore executeQuery:query];

如果要写入数据到苹果HealtkKit中,过程类似,下面的示例是写入步数到健康数据。(QQ中运动的步数和Keep中的步数都是从健康数据中获取的步数,而且没有过滤其它应用写入的数据,所以想要修改QQ或Keep中的步数,可以用自己的app写入步数数据,亲测有效)

①请求用户授权

  HKQuantityType *stepType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
  NSSet *shareTypes = [NSSet setWithObjects:stepType, nil];
 [self.healthStore requestAuthorizationToShareTypes:shareTypes readTypes:nil completion:^(BOOL success, NSError * _Nullable error) {

 }];

②写入数据

double step = [self.textField.text doubleValue];
HKQuantityType *stepType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKQuantity *stepQuantity = [HKQuantity quantityWithUnit:[HKUnit countUnit] doubleValue:step];
HKQuantitySample *stepSample = [HKQuantitySample quantitySampleWithType:stepType quantity:stepQuantity startDate:[self getTodayStartDate] endDate:[NSDate date]];
[self.healthStore saveObject:stepSample withCompletion:^(BOOL success, NSError * _Nullable error) {
  if (error) {
    NSLog(@"error: %@", error.localizedDescription);
  }
   dispatch_async(dispatch_get_main_queue(), ^{
     self.stateLabel.text = success ? @"成功" : @"失败";
  });
}];

项目中使用了HealthKit时,上架需要注意点:

  • Your app may not use information gained through the use of the HealthKit framework for advertising or similar services. Note that you may still serve advertising in an app that uses the HealthKit framework, but you cannot use data from the HealthKit store to serve ads.//你的应用不应该将HealthKit收集的数据用于广告或类似的服务。注意,可能在使用HealthKit框架应用中还是要服务广告,但是你不能使用HealthKit中的数据来服务广告。
  • You must not disclose any information gained through HealthKit to a third party without express permission from the user. Even with permission, you can only share information to a third party if they are also providing a health or fitness service to the user.// 在没有用户的明确允许下,你不能向第三方展示任何HealthKit收集的数据。即使用户允许,你也只能向提供健康或健身服务的第三方展示这些数据
  • You cannot sell information gained through HealthKit to advertising platforms, data brokers, or information resellers.// 你不能将HealthKit收集的数据出售给广告平台、数据代理人或者信息经销商
  • If the user consents, you may share his or her HealthKit data with a third party for medical research.// 如果用户允许,你可以将HealthKit数据共享给第三方用于医学研究。
  • You must clearly disclose to the user how you and your app will use their HealthKit data.//你必须明确说明,你和你的应用会怎样使用用户的HealthKit数据。

You must also provide a privacy policy for any app that uses the HealthKit framework. You can find guidance on creating a privacy policy at the following sites://你必须为每个使用HealthKit框架的应用提供一份隐私策略。你可以在以下网站找到创建隐私策略的指导:

1、Personal Health Record model (for non-HIPAA apps): http://www.healthit.gov/policy-researchers-implementers/personal-health-record-phr-model-privacy-notice

2、HIPAA model (for HIPAA covered apps): http://www.hhs.gov/ocr/privacy/hipaa/modelnotices.html

参考文章

https://developer.apple.com/documentation/healthkit#classes

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。

 类似资料:
  • 本文向大家介绍详解Java文件下载的几种实现方式,包括了详解Java文件下载的几种实现方式的使用技巧和注意事项,需要的朋友参考一下 Java文件下载的几种方式,具体如下:  //支持在线打开文件的一种方式 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持呐喊教程。

  • 本文向大家介绍详解Android 进程间通信的几种实现方式,包括了详解Android 进程间通信的几种实现方式的使用技巧和注意事项,需要的朋友参考一下 一、概述 由于应用程序之间不能共享内存。在不同应用程序之间交互数据(跨进程通讯),在Android SDK中提供了4种用于跨进程通讯的方式。 这4种方式正好对应于android系统中4种应用程序组件:Activity、Content Provide

  • 本文向大家介绍详解Android中点击事件的几种实现方式,包括了详解Android中点击事件的几种实现方式的使用技巧和注意事项,需要的朋友参考一下 在之前博文中多次使用了点击事件的处理实现,有朋友就问了,发现了很多按钮的点击实现,但有很多博文中使用的实现方式有都不一样,到底是怎么回事。今天我们就汇总一下点击事件的实现方式。 点击事件的实现大致分为以下三种: (1)Activity 实现接口方式实现

  • 本文向大家介绍详解VUE 定义全局变量的几种实现方式,包括了详解VUE 定义全局变量的几种实现方式的使用技巧和注意事项,需要的朋友参考一下 最近在学习VUE.js 中间涉及到JS全局变量,与其说是VUE的全局变量,不如说是模块化JS开发的全局变量。 1、全局变量专用模块 就是以一个特定模块来组织管理这些全局量,需要引用的地方导入该模块便好。 全局变量专用模块 Global.vue 模块里的变量用

  • 本文向大家介绍Linux下几种并发服务器的实现模式(详解),包括了Linux下几种并发服务器的实现模式(详解)的使用技巧和注意事项,需要的朋友参考一下 1>单线程或者单进程 相当于短链接,当accept之后,就开始数据的接收和数据的发送,不接受新的连接,即一个server,一个client 不存在并发。 2>循环服务器和并发服务器 1.循环服务器:一个server只能一次只能接收一个client,

  • 本文向大家介绍Webpack实现按需打包Lodash的几种方法详解,包括了Webpack实现按需打包Lodash的几种方法详解的使用技巧和注意事项,需要的朋友参考一下 前言 在数据操作时,Lodash 就是我的弹药库,不管遇到多复杂的数据结构都能用一些函数轻松拆解。 ES6 中也新增了诸多新的对象函数,一些简单的项目中 ES6 就足够使用了,但还是会有例外的情况引用了少数的 Lodash 函数。一