小小回顾
上一篇分析了项目的大体框架和跑步模块一些关键技术的实现。今天这篇是Running Life源码分析的第二篇,主要探讨以下问题:
1、HealthKit框架的使用;
2、贝塞尔曲线+帧动画实现优雅的数据展示界面;
3、实现一个view的复用机制解决内存暴涨的问题;
HealthKit
2014年6月2日召开的年度开发者大会上,苹果发布了一款新的移动应用平台,可以收集和分析用户的健康数据,苹果命名为“Healthkit ”。它管理着用户得健康数据,包括用户走路的步数、消耗的卡路里、体重、身高等等。相信大多数微信用户都知道微信运动这个功能,每天和好友PK自己的走路的步数,其实在iOS端的微信不做步数统计,它的数据源来自Healthkit。
那如何引入这个框架呢?
首先你要在你的App ID设置那里选择支持HealthKit,然后在Xcode做相应的设置:
这样基本的配置工作就结束了,接下就是码代码了:
我将SDK和框架的相关注册工作分离到“SDKInitProcess.h”这个文件,这样做是为了方便对SDK注册管理,此外还可以给AppDelegate瘦身。注册如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
-(void)initHealthKitSetting{ if (![HKHealthStore isHealthDataAvailable]) { NSLog(@"设备不支持healthKit"); } HKObjectType *walkingRuningDistance = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning]; NSSet *healthSet = [NSSet setWithObjects:walkingRuningDistance, nil]; //从健康应用中获取权限 [self.healthStore requestAuthorizationToShareTypes:nil readTypes:healthSet completion:^(BOOL success, NSError * _Nullable error) { if (success) { NSLog(@"获取距离权限成功"); } else { NSLog(@"获取距离权限失败"); } }]; } -(HKHealthStore *)healthStore{ if (!_healthStore) { _healthStore = [[HKHealthStore alloc]init]; } return _healthStore; } |
HKObjectType是你想从HealthKit框架获取的数据类型,针对我们的项目,我们需要获取距离数据。
我将获取HealthKit数据封装在工具类的HealthKitManager中,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
typedef void(^completeBlock)(id); typedef void(^errorBlock)(NSError*); /** * 健康数据管理对象 */ @interface HealthKitManager : NSObject /** * 全局管理类 * * @return */ + (HealthKitManager *)shareManager; /** * 获取某年某月的健康数据中路程数据(原生数据) * * @param year 年份 * @param month 月份 * @param block 成功回调 * @param errorBlock 失败回调 */ - (void)getDistancesWithYear:(NSInteger)year month:(NSInteger)month complete:(completeBlock) block failWithError:(errorBlock)errorBlock; /** * 获取某年某月的卡路里数据(计算数据) * * @param weight 体重 * @param year 年份 * @param month 月份 * @param block 成功回调 * @param errorBlock 失败回调 */ - (void)getKcalWithWeight:(float)weight year:(NSInteger)year month:(NSInteger)month complete:(completeBlock) block failWithError:(errorBlock)errorBlock; @end |
第一个方法是从HealthKit获取每个月份的距离数据,我来带大家看具体的实现代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
- (void)getDistancesWithYear:(NSInteger)year month:(NSInteger)month complete:(completeBlock) block failWithError:(errorBlock)errorBlock{ //想获取的数据类型,我们项目需要的是走路+跑步的距离数据 HKSampleType* sampleType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning]; //按数据开始日期排序 NSSortDescriptor* start = [NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierStartDate ascending:NO]; //以下操作是为了将字符串的日期转化为NSDate对象 NSDateFormatter* formatter = [[NSDateFormatter alloc]init]; [formatter setDateFormat:@"yy-MM-dd"]; NSString* startDateStr = [NSString stringWithFormat:@"%ld-%ld-%ld",(long)year,(long)month,(long)1]; //每个月第一天 NSDate* startDate = [formatter dateFromString:startDateStr]; //每个月的最后一天 NSDate* endDate = [[NSDate alloc]initWithTimeInterval:31*24*60*60-1 sinceDate:startDate]; //定义一个谓词逻辑,相当于sql语句,我猜测healthKit底层的数据存储应该用的也是CoreData NSPredicate* predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionNone]; //定义一个查询操作 HKSampleQuery* query = [[HKSampleQuery alloc]initWithSampleType:sampleType predicate:predicate limit:HKObjectQueryNoLimit sortDescriptors:@[start] resultsHandler:^(HKSampleQuery * _Nonnull query, NSArray<__kindof HKSample *> * _Nullable results, NSError * _Nullable error) { if (error) { if (errorBlock) { errorBlock(error); } }else{ //一个字典,键为日期,值为距离 NSMutableDictionary* multDict = [NSMutableDictionary dictionaryWithCapacity:30]; //遍历HKQuantitySample数组,遍历计算每一天的距离数据,然后放进multDict for (HKQuantitySample* sample in results) { NSString* dateStr = [formatter stringFromDate:sample.startDate] ; if ([[multDict allKeys]containsObject:dateStr]) { int distance = (int)[[multDict valueForKey:dateStr] doubleValue]; distance = distance + [sample.quantity doubleValueForUnit:[HKUnit meterUnit]]; [multDict setObject:@(distance) forKey:dateStr]; }else{ int distance = (int)[sample.quantity doubleValueForUnit:[HKUnit meterUnit]]; [multDict setObject: @(distance) forKey:dateStr]; } } NSArray* arr = multDict.allKeys; //multDict的键值按日期来排序:1号~31号 arr = [arr sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { NSString* str1 = [NSString stringWithFormat:@"%@",obj1]; NSString* str2 = [NSString stringWithFormat: |