详解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

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

(0)

相关推荐

  • IOS NSUserDefault 记住用户名及密码功能的实例代码

    一般的登录界面都会有一个记住密码的选项,要实现这个功能可以使用NSUserDefault,这里只是讲解明文的处理方式,虽然这样是有一定的风险性的但是目前只是了解如何实现这个功能: 首先声明一个NSUserDefault对象: let userDefaults = NSUserDefaults.standardUserDefaults() //本地操作所需 然后根据是否记住密码按钮的状态来判断是否要为用户名和密码设置值,如果是记住密码,那么需要取出需要记住的密码,并且为这两个TextField赋值

  • ios原生和react-native各种交互的示例代码

    需求:让一个表格视图中的cell能左滑删除,效果图如下: 目前RN中的ListView主要问题是复用,以及其他一些细节如索引视图.左滑删除.编辑等,要想在RN上自定义实现原生的这种效果尚有一定的问题,在必要时可以考虑使用原生的UITableView,数据从RN端传递 1.原生端编写表格控制器NativeTableViewController,暴露的属性如下 datas为表格数据源,另外一个为需要暴露给RN调用用方法. 2.框架只提供了暴露UIView给RN端的接口,所以需要制作一个中转UIVie

  • IOS 开发之实现取消tableView返回时cell选中的问题

    IOS 开发之实现取消tableView返回时cell选中的问题 在对表格UITableView操作时,有时当用户选中表格行后,需要自动取消选择.实现这种效果,其原理是选中表格行时,会调用 didSelectRowAtIndexPath方法,只要在这个方法中,调用performSelector执行取消选中表格行的方法. 示例代码如下: - (void) unselectCurrentRow { // Animate the deselection [self.tableView deselect

  • vue 里面使用axios 和封装的示例代码

    vue官方推荐使用 axios发送请求 首先上需求 1.需要封装全局调用 2.返回一个promise对象 3.错误全局统一处理 4.除了登录界面token带入头部 5.登录时候把用户信息自动存到vuex里面 首先上封装代码 /** * User: sheyude * Date: 2017/8/23 0023 * Time: 下午 13:15 * */ import axios from 'axios'; // 导入配置文件 配置文件就导入的请求的前缀地址 import {defaults} fr

  • iOS中利用CoreAnimation实现一个时间的进度条效果

    在iOS中实现进度条通常都是通过不停的设置progress来完成的,这样的进度条适用于网络加载(上传下载文件.图片等).但是对于录制视频这样的需求的话,如果是按照每秒来设置进度的话,显得有点麻烦,于是我就想直接用CoreAnimation来按时间做动画,只要设置最大时间,其他的就不用管了,然后在视频暂停与继续录制时,对动画进行暂停和恢复即可.录制视频的效果如下: 你可以在这里下载demo 那么接下来就是如何用CoreAnimation实现一个进度条控件了. 首先呢,让我们创建一个继承自CASha

  • iOS在Block中修改外部变量值的实现代码

    一,代码. - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // 第一种是可以修改 static 全局变量:第二种是可以修改用新关键字 __block 修饰的变量. __block int blockLocal = 100; static int staticLocal = 100; void (^aBlock)(

  • iOS 获取设备唯一标示符的方法详解

    在开发中会遇到应用需要记录设备标示,即使应用卸载后再安装也可重新识别的情况,在这写一种实现方式--读取设备的UUID(Universally Unique Identifier)并通过KeyChain记录. 首先iOS中获取设备唯一标示符的方法一直随版本的更新而变化.iOS 2.0版本以后UIDevice提供一个获取设备唯一标识符的方法uniqueIdentifier,通过该方法我们可以获取设备的序列号,这个也是目前为止唯一可以确认唯一的标示符.好景不长,因为该唯一标识符与手机一一对应,苹果觉得

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

    这篇文章介绍两种可以获取计步数据的方法,一种是采用CMPedometer获取手机计步器数据,另一种是采用HealthKit框架从手机健康App中获取计步数据.另外玩了一下写入数据到健康App.有描述不当之处,望指点. 花絮(用HealthKit框架构建app,写入数据到苹果健康app中,QQ和Keep等第三方app的运动数据都会随之改变,猜测它们的运动数据是直接从苹果健康app中获取,而且没有过滤掉其它数据来源.而微信运动的数据不会变,猜测其来源可能是使用CMPedometer类获取的,因为测试

  • 详解servlet调用的几种简单方式总结

    servlet调用的几种简单方式 这里总结的是我在学习web开发的过程中需要用到的几种比较常见的用于转发和调用servlet的方式,这些方式的使用率非常高.在网上总结了相关的方法,大多对于初学者不是特别的友好,自己总结了一下. 1.servlet直接转发到另一个servlet 我们在进行jsp页面点击按钮进行登录的时候,首先需要登录到进行登录检查的servlet,但是在下个jsp页面,我们需要那个页面通过servlet进行转发,所以需要从servlet直接跳转到另一个servlet,其实写法很简

  • 详解C++ 中的三种继承方式

    public 方式继承 基类成员对派生类的可见性对派生类来说,基类的公有成员和保护成员可见,基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态;基类的私有成员不可见,基类的私有成员仍然是私有的,派生类不可访问基类中的私有成员. 基类成员对派生类对象的可见性对派生类对象来说,基类的公有成员是可见的,其他成员是不可见的. 所以,在公有继承时,派生类的对象可以访问基类中的公有成员,派生类的成员函数可以访问基类中的公有成员和保护成员. 简单来说,派生类能访问基类的public, prote

  • 详解Java数组的四种拷贝方式

    目录 深拷贝与浅拷贝的区别 1.for循环进行拷贝 拷贝数值类型 拷贝引用类型 2.copyof/copyOfRange 拷贝数值类型 拷贝引用类型 3.arraycopy 拷贝数值类型 拷贝引用类型 4.clone 拷贝数值类型 拷贝引用类型 5.总结 深拷贝与浅拷贝的区别 假设现在有原数组A以及拷贝后的数组B,若是改变A中的某一个值,B数组随之相应的发生变化的拷贝方式称为浅拷贝,反之B数组不受影响,则称为深拷贝:简单总结一下两者的概念: 深拷贝:拷贝后,修改原数组,不会影响到新数组: 浅拷贝

  • 详解iOS中按钮点击事件处理方式

    写在前面 在iOS开发中,时常会用到按钮,通过按钮的点击来完成界面的跳转等功能.按钮事件的实现方式有多种,其中较为常用的是目标-动作对模式.但这种方式使得view与controller之间的耦合程度较高,不推荐使用: 另一种方式是代理方式,按钮的事件在view中绑定,controller作为view的代理实现代理方法. 目标-动作对实现方式 具体来说,假设我们有一个包含一个Button的veiw,view将Button放在头文件中,以便外部访问.然后controller将view作为自己的vie

  • 详解Android 进程间通信的几种实现方式

    一.概述 由于应用程序之间不能共享内存.在不同应用程序之间交互数据(跨进程通讯),在Android SDK中提供了4种用于跨进程通讯的方式. 这4种方式正好对应于android系统中4种应用程序组件:Activity.Content Provider.Broadcast和Service.其中Activity可以跨进程调用其他应用程序的Activity:Content Provider可以跨进程访问其他应用程序中的数据(以Cursor对象形式返回),当然,也可以对其他应用程序的数据进行增.删.改操

  • 详解Java文件下载的几种实现方式

    Java文件下载的几种方式,具体如下: public HttpServletResponse download(String path, HttpServletResponse response) { try { // path是指欲下载的文件的路径. File file = new File(path); // 取得文件名. String filename = file.getName(); // 取得文件的后缀名. String ext = filename.substring(filena

  • 详解SpringBoot工程的三种搭建方式

    SpringBoot的主要目的是简化配置文件,通过少量配置即可运行Java程序,其强大的自动配置功能帮助开发者轻松实现配置装配,通过引入SpringBoot的 starter 就能实现想要的功能,不需要额外的配置. 目前SpringBoot工程有三种搭建方式: 通过Spring Initializr创建 通过IDEA创建工程 手动创建工程 官方生成工具 Spring团队提供一个非常方便的网页用于生成SpringBoot工程,打开浏览器进入Spring Initializr: 工程生成参数列表:

  • 详解IOS 单例的两种方式

    详解IOS 单例的两种方式 方法一: #pragma mark - #pragma mark sharedSingleton methods //单例函数 static RtDataModel *sharedSingletonManager = nil; + (RtDataModel *)sharedManager { @synchronized(self) { if (sharedSingletonManager == nil) { sharedSingletonManager = [[sel

  • 详解IOS判断当前网络状态的三种方法

    在项目中,为了好的用户体验,有些场景必须线判断网络状态,然后才能决定该干嘛.比如视频播放,需要线判断是Wifi还是4G,Wifi直接播放,4G先提示用户.获取网络状态的方法大概有三种: 1. Reachability 这是苹果的官方演示demo中使用到的方法,我们可以到苹果官方文档里下载Demo(点击左上角Download Sample Code 即可下载),然后把Demo里的Reachability.h和.m考到自己项目中,并在Build Phases 的 Link Binary 添加Syst

随机推荐