iOS開發(fā)-讀取“健康”中的步數(shù)和步行+跑步距離

借鑒于
http://www.csdn.net/article/2015-01-23/2823686-healthkit-tutorial-with-swift/4

  • 注:iOS10遇到的錯誤:
Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: 'NSHealthUpdateUsageDescription must be set in the app's Info.plist
in order to request write authorization.'

解決辦法:
我們要在info.plist文件中聲明蘋果健康的使用權(quán)限税肪,所以在info.plist中添加以下key就可以了年鸳。請求寫入和請求讀取都需要添加!

請求寫入
  <key>NSHealthUpdateUsageDescription</key>
<string>some string value stating the reason</string>
請求讀取
   <key>NSHealthShareUsageDescription</key>
    <string>some string value stating the reason</string>

iOS 10 因蘋果健康導(dǎo)致閃退 crash - 借鑒與簡書作者文章 找到的解決辦法,謝謝

1、第一步首先需要開啟HealthKit


HealthKit.png

2牵舱、新建一個HealthKitManage類,繼承于NSObject

  • (1)首先在.h文件里面聲明一個屬性
@property (nonatomic, strong) HKHealthStore *healthStore;
  • (2)導(dǎo)入頭文件
#import <HealthKit/HealthKit.h>
#import <UIKit/UIDevice.h>

#define HKVersion [[[UIDevice currentDevice] systemVersion] doubleValue]
#define CustomHealthErrorDomain @"com.sdqt.healthError"
  • (3)創(chuàng)建單例方法
+(id)shareInstance
{
    static id manager ;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[[self class] alloc] init];
    });
    return manager;
}
  • (4)檢查是否支持獲取健康數(shù)據(jù)
/*
 *  @brief  檢查是否支持獲取健康數(shù)據(jù)
 */
- (void)authorizeHealthKit:(void(^)(BOOL success, NSError *error))compltion
{
    if(HKVersion >= 8.0)
    {
        if (![HKHealthStore isHealthDataAvailable]) {
            NSError *error = [NSError errorWithDomain: @"com.raywenderlich.tutorials.healthkit" code: 2 userInfo: [NSDictionary dictionaryWithObject:@"HealthKit is not available in th is Device"                                                                      forKey:NSLocalizedDescriptionKey]];
            if (compltion != nil) {
                compltion(false, error);
            }
            return;
        }
        if ([HKHealthStore isHealthDataAvailable]) {
            if(self.healthStore == nil)
                self.healthStore = [[HKHealthStore alloc] init];
            /*
             組裝需要讀寫的數(shù)據(jù)類型
             */
            NSSet *writeDataTypes = [self dataTypesToWrite];
            NSSet *readDataTypes = [self dataTypesRead];
            
            /*
             注冊需要讀寫的數(shù)據(jù)類型帆喇,也可以在“健康”APP中重新修改
             */
            [self.healthStore requestAuthorizationToShareTypes:writeDataTypes readTypes:readDataTypes completion:^(BOOL success, NSError *error) {
                
                if (compltion != nil) {
                    NSLog(@"error->%@", error.localizedDescription);
                    compltion (success, error);
                }
            }];
        }
    }
    else {
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"iOS 系統(tǒng)低于8.0"                                                                      forKey:NSLocalizedDescriptionKey];
        NSError *aError = [NSError errorWithDomain:CustomHealthErrorDomain code:0 userInfo:userInfo];
        compltion(0,aError);
    }
}

/*!
 *  @brief  寫權(quán)限
 *  @return 集合
 */
- (NSSet *)dataTypesToWrite
{
    HKQuantityType *heightType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeight];
    HKQuantityType *weightType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
    HKQuantityType *temperatureType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyTemperature];
    HKQuantityType *activeEnergyType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierActiveEnergyBurned];
    
    return [NSSet setWithObjects:heightType, temperatureType, weightType,activeEnergyType,nil];
}

/*!
 *  @brief  讀權(quán)限
 *  @return 集合
 */
- (NSSet *)dataTypesRead
{
    HKQuantityType *heightType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeight];
    HKQuantityType *weightType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
    HKQuantityType *temperatureType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyTemperature];
    HKCharacteristicType *birthdayType = [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierDateOfBirth];
    HKCharacteristicType *sexType = [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierBiologicalSex];
    HKQuantityType *stepCountType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
    HKQuantityType *distance = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];
    HKQuantityType *activeEnergyType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierActiveEnergyBurned];
    
    return [NSSet setWithObjects:heightType, temperatureType,birthdayType,sexType,weightType,stepCountType, distance, activeEnergyType,nil];
}
  • (5)讀取步數(shù)
//獲取步數(shù)
- (void)getStepCount:(void(^)(double value, NSError *error))completion
{
    HKQuantityType *stepType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
    NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:NO];
    
    // Since we are interested in retrieving the user's latest sample, we sort the samples in descending order, and set the limit to 1. We are not filtering the data, and so the predicate is set to nil.
    HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:stepType predicate:[HealthKitManage predicateForSamplesToday] limit:HKObjectQueryNoLimit sortDescriptors:@[timeSortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
            if(error)
            {
                completion(0,error);
            }
            else
            {
                NSInteger totleSteps = 0;
                for(HKQuantitySample *quantitySample in results)
                {
                    HKQuantity *quantity = quantitySample.quantity;
                    HKUnit *heightUnit = [HKUnit countUnit];
                    double usersHeight = [quantity doubleValueForUnit:heightUnit];
                    totleSteps += usersHeight;
                }
                NSLog(@"當(dāng)天行走步數(shù) = %ld",(long)totleSteps);
                completion(totleSteps,error);
            }
    }];
    
    [self.healthStore executeQuery:query];
}
  • (6)讀取步行+跑步距離
//獲取公里數(shù)
- (void)getDistance:(void(^)(double value, NSError *error))completion
{
    HKQuantityType *distanceType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];
    NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:NO];
    HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:distanceType predicate:[HealthKitManage predicateForSamplesToday] limit:HKObjectQueryNoLimit sortDescriptors:@[timeSortDescriptor] resultsHandler:^(HKSampleQuery * _Nonnull query, NSArray<__kindof HKSample *> * _Nullable results, NSError * _Nullable error) {
        
        if(error)
        {
            completion(0,error);
        }
        else
        {
            double totleSteps = 0;
            for(HKQuantitySample *quantitySample in results)
            {
                HKQuantity *quantity = quantitySample.quantity;
                HKUnit *distanceUnit = [HKUnit meterUnitWithMetricPrefix:HKMetricPrefixKilo];
                double usersHeight = [quantity doubleValueForUnit:distanceUnit];
                totleSteps += usersHeight;
            }
            NSLog(@"當(dāng)天行走距離 = %.2f",totleSteps);
            completion(totleSteps,error);
        }
    }];
    [self.healthStore executeQuery:query];
}
  • (7)逼龟、NSPredicate當(dāng)天時間段的方法實(shí)現(xiàn)
/*!
 *  @brief  當(dāng)天時間段
 *
 *  @return 時間段
 */
+ (NSPredicate *)predicateForSamplesToday {
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDate *now = [NSDate date];
    NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:now];
    [components setHour:0];
    [components setMinute:0];
    [components setSecond: 0];
    
    NSDate *startDate = [calendar dateFromComponents:components];
    NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0];
    NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionNone];
    return predicate;
}

3、在控制器里展示出來讀取的數(shù)據(jù)
(1)汹想、首先導(dǎo)入頭文件外邓,并添加Label

#import "HealthKitManage.h"
@interface ViewController ()

@end

@implementation ViewController
{
    UILabel *stepLabel;
    UILabel *distanceLabel;
}

(2)創(chuàng)建界面,展示數(shù)據(jù)

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
    btn1.frame = CGRectMake(50, 100, 100, 40);
    [btn1 setTitle:@"計步" forState:UIControlStateNormal];
    btn1.backgroundColor = [UIColor cyanColor];
    [self.view addSubview:btn1];
    [btn1 addTarget:self action:@selector(onClickBtn1) forControlEvents:UIControlEventTouchUpInside];
    
    UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom];
    btn2.frame = CGRectMake(50, 160, 100, 40);
    [btn2 setTitle:@"距離" forState:UIControlStateNormal];
    btn2.backgroundColor = [UIColor cyanColor];
    [self.view addSubview:btn2];
    [btn2 addTarget:self action:@selector(onClickBtn2) forControlEvents:UIControlEventTouchUpInside];
    
    stepLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 220, 200, 40)];
    stepLabel.backgroundColor = [UIColor cyanColor];
    [self.view addSubview:stepLabel];
    
    distanceLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 280, 200, 40)];
    distanceLabel.backgroundColor = [UIColor cyanColor];
    [self.view addSubview:distanceLabel];
}

- (void)onClickBtn1
{
    HealthKitManage *manage = [HealthKitManage shareInstance];
    [manage authorizeHealthKit:^(BOOL success, NSError *error) {
        
        if (success) {
            NSLog(@"success");
            [manage getStepCount:^(double value, NSError *error) {
                NSLog(@"1count-->%.0f", value);
                NSLog(@"1error-->%@", error.localizedDescription);
                dispatch_async(dispatch_get_main_queue(), ^{
                    stepLabel.text = [NSString stringWithFormat:@"步數(shù):%.0f步", value];
                });
                
            }];
        }
        else {
            NSLog(@"fail");
        }
    }];
}

- (void)onClickBtn2
{
    HealthKitManage *manage = [HealthKitManage shareInstance];
    [manage authorizeHealthKit:^(BOOL success, NSError *error) {
        
        if (success) {
            NSLog(@"success");
            [manage getDistance:^(double value, NSError *error) {
                NSLog(@"2count-->%.2f", value);
                NSLog(@"2error-->%@", error.localizedDescription);
                dispatch_async(dispatch_get_main_queue(), ^{
                    distanceLabel.text = [NSString stringWithFormat:@"公里數(shù):%.2f公里", value];
                });
                
            }];
        }
        else {
            NSLog(@"fail");
        }
    }];
}

4古掏、展示成果


成果展示圖.png

二损话、使用CoreMotion獲取計步信息
目前只使用到計步功能,個人感覺CoreMotion與HealthKit在計步方面的區(qū)別:HealthKit中計步信息可以通過“健康A(chǔ)pp”進(jìn)行修改。
1丧枪、info.plist中

<key>NSMotionUsageDescription</key>
    <string>Privacy - Motion Usage Description</string>

2光涂、上代碼

#import <CoreMotion/CoreMotion.h>
@property (nonatomic, strong) CMPedometer            *pedometer;

    _pedometer = [[CMPedometer alloc] init];
    if ([CMPedometer isStepCountingAvailable]) {
        //獲取昨天的步數(shù)與距離數(shù)據(jù)
        [_pedometer queryPedometerDataFromDate:[NSDate dateWithTimeIntervalSinceNow:-60*60*24*2] toDate:[NSDate dateWithTimeIntervalSinceNow:-60*60*24*1] withHandler:^(CMPedometerData * _Nullable pedometerData, NSError * _Nullable error) {
            if (error) {
                NSLog(@"error===%@",error);
            }
            else {
                NSLog(@"步數(shù)===%@",pedometerData.numberOfSteps);
                NSLog(@"距離===%@",pedometerData.distance);
            }
        }];
    } else {
        NSLog(@"不可用===");
    }

借鑒兩篇文章代碼,多謝
CMPedometer(計步器)使用拧烦,獲取用戶行走步數(shù)忘闻、距離等信息
華山論劍之淺談iOS計步功能

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市恋博,隨后出現(xiàn)的幾起案子齐佳,更是在濱河造成了極大的恐慌,老刑警劉巖债沮,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炼吴,死亡現(xiàn)場離奇詭異,居然都是意外死亡疫衩,警方通過查閱死者的電腦和手機(jī)硅蹦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闷煤,“玉大人童芹,你說我怎么就攤上這事〔芸” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵饲宛,是天一觀的道長皆愉。 經(jīng)常有香客問我,道長艇抠,這世上最難降的妖魔是什么幕庐? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮家淤,結(jié)果婚禮上异剥,老公的妹妹穿的比我還像新娘。我一直安慰自己絮重,他們只是感情好冤寿,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著青伤,像睡著了一般督怜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上狠角,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天号杠,我揣著相機(jī)與錄音,去河邊找鬼。 笑死姨蟋,一個胖子當(dāng)著我的面吹牛屉凯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播眼溶,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼悠砚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了偷仿?” 一聲冷哼從身側(cè)響起哩簿,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎酝静,沒想到半個月后节榜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡别智,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年宗苍,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片薄榛。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡讳窟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出敞恋,到底是詐尸還是另有隱情丽啡,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布硬猫,位于F島的核電站补箍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏啸蜜。R本人自食惡果不足惜坑雅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望衬横。 院中可真熱鬧裹粤,春花似錦、人聲如沸蜂林。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽噪叙。三九已至突那,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間构眯,已是汗流浹背愕难。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人猫缭。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓葱弟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親猜丹。 傳聞我的和親對象是個殘疾皇子芝加,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容