1. 程式人生 > >開源專案Running Life 原始碼分析(二)

開源專案Running Life 原始碼分析(二)

小小回顧

上一篇分析了專案的大體框架和跑步模組一些關鍵技術的實現。今天這篇是Running Life原始碼分析的第二篇,主要探討以下問題:
1、HealthKit框架的使用;
2、貝塞爾曲線+幀動畫實現優雅的資料展示介面;
3、實現一個view的複用機制解決記憶體暴漲的問題;

HealthKit

2014年6月2日召開的年度開發者大會上,蘋果釋出了一款新的移動應用平臺,可以收集和分析使用者的健康資料,蘋果命名為“Healthkit ”。它管理著使用者得健康資料,包括使用者走路的步數、消耗的卡路里、體重、身高等等。相信大多數微信使用者都知道微信運動這個功能,每天和好友PK自己的走路的步數,其實在iOS端的微信不做步數統計,它的資料來源來自Healthkit。
那如何引入這個框架呢?
首先你要在你的App ID設定那裡選擇支援HealthKit,然後在Xcode做相應的設定:

11563e716367ac88566233dced5f57e140
這樣基本的配置工作就結束了,接下就是碼程式碼了:
我將SDK和框架的相關注冊工作分離到“SDKInitProcess.h”這個檔案,這樣做是為了方便對SDK註冊管理,此外還可以給AppDelegate瘦身。註冊如下:
1234567891011121314151617181920212223242526272829 -(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:^(BOOLsuccess,NSError *_Nullable error){if(success){NSLog(@"獲取距離許可權成功");}else{NSLog(@"獲取距離許可權失敗");}}];}-(HKHealthStore *)healthStore{if(!_healthStore){_healthStore=[[HKHealthStore alloc]init];}return_healthStore;}

HKObjectType是你想從HealthKit框架獲取的資料型別,針對我們的專案,我們需要獲取距離資料。
我將獲取HealthKit資料封裝在工具類的HealthKitManager中,程式碼如下:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546 typedefvoid(^completeBlock)(id);typedefvoid(^errorBlock)(NSError*);/** *  健康資料管理物件 */@interfaceHealthKitManager:NSObject/** *  全域性管理類 * *  @return */+(HealthKitManager *)shareManager;/** *  獲取某年某月的健康資料中路程資料(原生資料) * *  @param year       年份 *  @param month      月份 *  @param block      成功回撥 *  @param errorBlock 失敗回撥 */-(void)getDistancesWithYear:(NSInteger)yearmonth:(NSInteger)monthcomplete:(completeBlock)blockfailWithError:(errorBlock)errorBlock;/** *  獲取某年某月的卡路里資料(計算資料) * *  @param weight     體重 *  @param year       年份 *  @param month      月份 *  @param block      成功回撥 *  @param errorBlock 失敗回撥 */-(void)getKcalWithWeight:(float)weightyear:(NSInteger)yearmonth:(NSInteger)monthcomplete:(completeBlock)blockfailWithError:(errorBlock)errorBlock;@end

第一個方法是從HealthKit獲取每個月份的距離資料,我來帶大家看具體的實現程式碼:

Objective-C
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980 -(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=[[NSDateFormatteralloc]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=[[NSDatealloc]initWithTimeInterval:31*24*60*60-1 sinceDate:startDate];//定義一個謂詞邏輯,相當於sql語句,我猜測healthKit底層的資料儲存應該用的也是CoreDataNSPredicate*predicate=[HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionNone];//定義一個查詢操作HKSampleQuery*query=[[HKSampleQueryalloc]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陣列,遍歷計算每一天的距離資料,然後放進multDictfor(HKQuantitySample*sample inresults){NSString*dateStr=[formatter stringFromDate:sample.startDate];if([[multDict allKeys]containsObject:dateStr]){intdistance=(int)[[multDict valueForKey:dateStr] doubleValue];distance=distance+[sample.quantity doubleValueForUnit:[HKUnitmeterUnit]];[multDict setObject:@(distance) forKey:dateStr];}else{intdistance=(int)[sample.quantity doubleValueForUnit:[HKUnitmeterUnit]];[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:@"%@",obj2];intnum1=[[str1 substringFromIndex:6] intValue];intnum2=[[str2 substringFromIndex:6] intValue];if(num1<num2){returnNSOrderedAscending;}else{returnNSOrderedDescending;}}];//按排序好的鍵取得值裝進resultArr返回給業務層NSMutableArray*resultArr=[NSMutableArray arrayWithCapacity:30];for(NSString*key inarr){[resultArr addObject:multDict[key]];}if(block){block(resultArr);}}}];//執行查詢[self.healthStore executeQuery:query];}

第一個方法相關講解我已經寫在註釋裡;
第二個方法是根據healthKit獲取的距離資料計算出卡路里資料,它是根據以下公式計算得的:
卡路里(kcal) = 公里數(km) 體重(kg) 1.036
那麼HealthKit的相關講解就到這裡,想了解更多內容可以戳這裡

資料展示介面

效果如下:

效果圖

上面的日曆控制元件不難實現,由NSCalendar + UICollectionView實現,就不展開講了。這裡主要分析的下面那個座標圖以及動畫效果如何實現。
實現的關鍵點:CAShapeLayer、UIBezierPath、CABasicAnimation
實現的程式碼在”RecordShowView.m”這個類中:

座標系的繪畫:

12345678 -(void)drawBaseView{