HealthKit教程 Swift版 Part2——锻炼信息

(原文:HealthKit Tutorial with Swift: Workouts 作者:Ernesto García
译者:Mr_cyz )

欢迎回到我们的HealthKit系列教程!

在我们系列教程的第一篇中译版)中,你已经学到了使用HealthKit开发的基础:读写数据。

在这第二篇,同时也是最后一篇中,你将会学到怎么样处理一种更复杂的数据类型:锻炼与健身的信息(Workout)。

这篇教程从上一篇教程结束的地方开始,所以如果你还没有上一篇中完成的工程,你可以从这里下载

开始

单从身体方面来说,一个人的锻炼与健身信息由一段时间内做过的身体素质锻炼组成。而再加上数字化的方面来看,你可以通过下面的这些基本属性来得到一条锻炼信息:

  • 运动的种类。例如:跑步、骑行、冰壶等
  • 距离
  • 起止时间
  • 持续时间
  • 运动时消耗的能量。

数字化的领域就是指的HealthKit了,一条锻炼信息就是一个其他类型的数据采样信息(samples)的容器。例如,你可以添加一组数据,来表示在你运动时的心率。如果你打算做一款健康类型的app,那么这将是一个功能强大的特性。

在该工程中,你将会存储跑步锻炼时的信息,当然你也可以很容易地改变活动的种类来表示其他类型的锻炼信息。

我们的起始项目中已经包含了一个视图控制器,来为你提供查看健康信息的入口。导航到Workouts栏然后点击+按钮你就能看到了。

当你停止运动时,会在该页面收集信息并且展示到Workouts视图控制器中。你需要使用这些信息来创建一条健康信息。

保存锻炼信息

首先,你将创建一个方法来保存跑步时的信息。打开HealthManager.swift,添加如下方法:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

func saveRunningWorkout(startDate:NSDate , endDate:NSDate , distance:Double, distanceUnit:HKUnit , kiloCalories:Double,

  completion: ( (Bool, NSError!) -> Void)!) {

 

    // 1. Create quantities for the distance and energy burned

    let distanceQuantity = HKQuantity(unit: distanceUnit, doubleValue: distance)

    let caloriesQuantity = HKQuantity(unit: HKUnit.kilocalorieUnit(), doubleValue: kiloCalories)

 

    // 2. Save Running Workout

    let workout = HKWorkout(activityType: HKWorkoutActivityType.Running, startDate: startDate, endDate: endDate, duration: abs(endDate.timeIntervalSinceDate(startDate)), totalEnergyBurned: caloriesQuantity, totalDistance: distanceQuantity, metadata: nil)

    healthKitStore.saveObject(workout, withCompletion: { (success, error) -> Void in

      if( error != nil  ) {

        // Error saving the workout

        completion(success,error)

      }

      else {

        // Workout saved

        completion(success,nil)

 

      }

    })

}

这段代码做了什么呢?让我们一行一行地分析:

  1. 与你之前创建BMI类型的身体素质信息一样,创建两个身体素质类型的对象,分别设置为距离和能量类型,注意使用double类型的数据以及选择合适的单位。
  2. 使用起止时间、持续时间以及你刚刚创建好的代表距离和消耗的能量的身体素质信息来创建一个HKWorkOut对象,然后通过调用HKHealthKit Store的saveObject方法来将健康信息保存到Store中,结果信息或者是错误信息将传到completion回调中。

现在你需要在Workouts视图控制器中调用该方法,打开WorkoutsTableViewController.swift然后找到unwindToSegue()方法,当你在新的Workout界面按下Done的时候就会调用该方法。

将下面这一行:


1

println("TODO: Save workout in Health Store")

替换为:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

if let addViewController:AddWorkoutTableViewController = segue.sourceViewController as? AddWorkoutTableViewController {

 

// 1. Set the Unit type

var hkUnit = HKUnit.meterUnitWithMetricPrefix(.Kilo)

if distanceUnit == .Miles {

  hkUnit = HKUnit.mileUnit()

}

 

// 2. Save the workout

self.healthManager?.saveRunningWorkout(addViewController.startDate!, endDate: addViewController.endDate!, distance: addViewController.distance , distanceUnit:hkUnit, kiloCalories: addViewController.energyBurned!, completion: { (success, error ) -> Void in

  if( success )

  {

    println("Workout saved!")

  }

  else if( error != nil ) {

    println("\(error)")

  }

})

}

  1. 首先,创建一个合适的单位对象,用户通过分段控件(segment control)设置distanceUnit的值,从而选择距离单位的类型。这段代码检查distanceUnit的值来决定使用合适的HKUnit。
  2. 创建完单位之后调用saveRunningWorkoutMethod()方法来保存这些健康信息,包括开始日期、结束日期、持续时间以及能量消耗。

编译并运行,点击 + 按钮,像下面视图中展示的那样填入数据。

哇!26.2英里(42.195千米),而且是两小时零一分钟之内,我想你刚刚在编码的过程中打破了马拉松世界记录。你真是个天才!

完成之后点击Done,如果一切顺利,你将在Xcode的控制台中看到如下信息:


1

Workout saved!

太棒了!你的健康信息已经成功地被保存到HealthKit Store中了,如果你想的话你可以重复刚才的操作来添加更多的健康信息。

查询健康信息

如果你运行你的应用,然后进入Workouts视图控制器,你不会看到任何之前你在视图中创建好的健康信息。

你需要加入一些代码来读取并且展示这些信息。为了能够读取到这些信息,你需要创建一个HKSampleQuery对象,然后执行这个查询来获取数据。

这与之前读取身高和体重的代码将会非常类似,为什么试着自己写一下呢?

在HealthManager.swift中创建一个方法,利用HKWorkoutActivityType类型来查询健康信息,将结果按照起始日期降序的顺序排好序,在completion回调中获取返回结果。使用如下方法声明:


1

func readRunningWorkOuts(completion: (([AnyObject]!, NSError!) -> Void)!)

内部实现:readRunningWorkOuts的实现:

打开HealthManager.swift然后添加这个方法:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

func readRunningWorkOuts(completion: (([AnyObject]!, NSError!) -> Void)!) {

 

  // 1. Predicate to read only running workouts

  let predicate =  HKQuery.predicateForWorkoutsWithWorkoutActivityType(HKWorkoutActivityType.Running)

  // 2. Order the workouts by date

  let sortDescriptor = NSSortDescriptor(key:HKSampleSortIdentifierStartDate, ascending: false)

  // 3. Create the query

  let sampleQuery = HKSampleQuery(sampleType: HKWorkoutType.workoutType(), predicate: predicate, limit: 0, sortDescriptors: [sortDescriptor])

    { (sampleQuery, results, error ) -> Void in

 

      if let queryError = error {

        println( "There was an error while reading the samples: \(queryError.localizedDescription)")

      }

      completion(results,error)

  }

  // 4. Execute the query

  healthKitStore.executeQuery(sampleQuery)

 

}

这段代码与你之前读身高和体重的代码非常类似:

  1. 首先创建一个谓词对象,HKQuery提供了一个方法:predicateForWorkoutsWithWorkoutActivityType()来创建查询锻炼信息用到的谓词。通过使用HKWorkoutActivityType.Running参数来指定你希望查询的是跑步类型的锻炼信息。如果你想查询其他类型的锻炼信息,例如骑行、游泳,你只需要在创建该谓词的时候改变查询类型即可。
  2. 创建一个排序标识符来让结果信息按照日期降序排列。
  3. 创建HKSampleQuery对象,调用executeQuery()方法来获得结果。

你需要将健康信息展示到列表中,所以你要调用该方法并实现UITableView的数据源协议,所以,接下来打开WorkoutsTableViewController.swift。

你需要在该类中创建一个数组类型的属性来保存所有的健康信息,将这一行代码添加到WorkoutsTableViewController顶部声明其他属性的附近:


1

var workouts = [HKWorkout]()

然后,添加该方法,作用是当视图刚刚出现时读取健康信息:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

public override func viewWillAppear(animated: Bool) {

  super.viewWillAppear(animated)

 

  healthManager?.readRunningWorkOuts({ (results, error) -> Void in

    if( error != nil )

    {

      println("Error reading workouts: \(error.localizedDescription)")

      return;

    }

    else

    {

      println("Workouts read successfully!")

    }

 

    //Kkeep workouts and refresh tableview in main thread

    self.workouts = results as [HKWorkout]

    dispatch_async(dispatch_get_main_queue(), { () -> Void in

      self.tableView.reloadData()

    });

 

  })

}

这里调用了你刚刚创建好的方法readWorkouts。当收到结果后,将结果保存到workouts中,然后在主线程中刷新列表数据。

现在,你需要添加列表的数据源协议中的方法,在WorkoutsTableViewController中添加tableView:numberOfRowsInSection方法的实现:


1

2

3

public override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

  return  workouts.count

}

这很直接明了,当列表询问有多少行时,你只需要返回你从Store中读到的健康信息的数量即可。

现在是时候填充列表的cell了,你需要实现在列表的数据源协议中的方法tableView:cellForRowAtIndexPath,将如下代码添加到WorkoutsTableViewController类中:


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

public override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

  let cell = tableView.dequeueReusableCellWithIdentifier("workoutcellid", forIndexPath: indexPath) as UITableViewCell

 

 

  // 1. Get workout for the row. Cell text: Workout Date

  let workout  = workouts[indexPath.row]

  let startDate = dateFormatter.stringFromDate(workout.startDate)

  cell.textLabel!.text = startDate

 

  // 2. Detail text: Duration - Distance 

  // Duration

  var detailText = "Duration: " + durationFormatter.stringFromTimeInterval(workout.duration)!

  // Distance in Km or miles depending on user selection

  detailText += " Distance: "

  if distanceUnit == .Kilometers {

    let distanceInKM = workout.totalDistance.doubleValueForUnit(HKUnit.meterUnitWithMetricPrefix(HKMetricPrefix.Kilo))

    detailText += distanceFormatter.stringFromValue(distanceInKM, unit: NSLengthFormatterUnit.Kilometer)

  }

  else {

    let distanceInMiles = workout.totalDistance.doubleValueForUnit(HKUnit.mileUnit())

    detailText += distanceFormatter.stringFromValue(distanceInMiles, unit: NSLengthFormatterUnit.Mile)

 

  }

  // 3. Detail text: Energy Burned 

  let energyBurned = workout.totalEnergyBurned.doubleValueForUnit(HKUnit.jouleUnit())

  detailText += " Energy: " + energyFormatter.stringFromJoules(energyBurned)

  cell.detailTextLabel?.text = detailText;

 

 

  return cell

}

解释一下上面的代码:

  1. 这段代码获得当前这一行的健康信息,然后将起始日期格式化并展示在cell中的text lable上。为了将日期格式化,这里使用了之前在初始工程中为你创建好的NSDateFormatter类。
  2. 通过距离和消耗的能量来定义展示在detail label中的字符串。距离信息以英里或者千米的形式展示出来,这取决于用户的选择(被保存在distanceUnit属性中)。获取距离的double类型的值,然后基于该属性的值传入一个合适的距离单位。接下来,使用NSDistanceFormatter类,传入合适的单位,调用stringFromValue:unit方法,将距离信息格式化为本地字符串。对于该条健康信息的持续时间,使用一个NSDateComponentsFormatter类对象。所有这些格式转换器都是在初始工程中为你预先创建好的。
  3. 使用NSEnergyFormatter将消耗的能量也转换为字符串,该字符串最终将被展示到detail label中。

编译并运行该app,导航到Workouts页面,现在你应该能在列表中看到之前储存好的健康信息了。

酷!你已经如期将所有的健康信息展示出来了。现在,点击分段控件,检验一下,距离以英里或以千米为单位来展示,取决于你的选择。

如果你打开Health应用,你不会在任何地方找到这些信息,它就是这样设计的,因为Health应用只展示数据采样信息,并不会展示我们的健康信息。

然而,你是可以让用户看到这些关于健康的信息的,你只需要将它们与一些采样信息关联起来即可。一款健康管家应用如果不带有健康与锻炼的信息,就好比一教练机没有警报装置一样。因此你应该将这些数据结合起来。

把采样信息添加到健康信息中

作为最后一步,你将把距离和消耗的能量这些采样信息添加到健康信息中。

打开HealthManager.swift然后前往saveRunningWorkout(),在成功执行的回调闭包中,将下面这两行:


1

2

// Workout saved

completion(success,nil)

替换为:


1

2

3

4

5

6

7

// if success, then save the associated samples so that they appear in the HealthKit

let distanceSample = HKQuantitySample(type: HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning), quantity: distanceQuantity, startDate: startDate, endDate: endDate)

let caloriesSample = HKQuantitySample(type: HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned), quantity: caloriesQuantity, startDate: startDate, endDate: endDate)

 

self.healthKitStore.addSamples([distanceSample,caloriesSample], toWorkout: workout, completion: { (success, error ) -> Void in

  completion(success, error)

})

到现在,你可能对这些代码已经非常熟悉了,甚至可能到了你会认为你在做重复的事情的地步了。但是为了让这些更加清晰,这里做一下解释:

第一步中,创建了两个quantity sample类型的对象,一个用DistanceWalkingRunning类型来代表跑步的距离。另一个用ActiveEnergyBurned类型来代表消耗的卡路里,然后调用HealthKit Store的方法addsamples:ToWorkout将这两个数据添加到健康信息中。

编译并运行该应用,添加一到两条健康信息,然后关掉应用。既然这些健康信息已经和距离以及消耗的能量的数据关联起来了,你就可以在Health应用中看到它们了。

打开Health应用,前往Health Data栏,在这里选择Fitness选项,然后选择Walking+Running Distance或者Active Calories来检查一下数据是否保存到那里了。你将会看到类似如下界面:

太棒了!现在你已经有获得最重要的健康信息并将其保存到HealthKit Store中的能力了。

现在该干什么?

这里你可以下载包含这篇教程中所有代码的工程。

!重要!:如果你想使用上面的示例工程,在使用HealthKit之前需要进行一些设置,因为该工程绑定了一个示例用Bundle ID,你需要将其修改为你自己的Bundle ID,选择你的开发团队,然后将Target栏中Capabilities菜单下的HealthKit的开关由OFF变为ON。

详见上述“开始”部分和“授权与许可”部分。

但愿本篇教程能够就HealthKit的基础概念给你一些对认识与了解,让你明白怎么样在自己的应用中使用HealthKit。要了解更多关于HealthKit的相关知识,这里有一些相关资源:

在浏览过这些文档和视频之后,你应该准备好前往HealthKit更深入的方面,然后对本篇的这个应用做一些改善。例如,你可以添加一些新的类型的数据采样信息或者是健康信息,使用HKStatisticsQuery计算统计结果,或者通过HKObserverQuery来观察Store中信息的改变。

我希望你能喜欢这篇教程,和以前一样,如果你有任何问题或者评论,请参与下方的讨论!

(本文为CocoaChina组织翻译,本译文权利归译者所有,未经允许禁止转载。)

时间: 2024-10-13 11:34:45

HealthKit教程 Swift版 Part2——锻炼信息的相关文章

iOS游戏框架Sprite Kit基础教程——Swift版上册

iOS游戏框架Sprite Kit基础教程--Swift版上册 试读下载地址:http://pan.baidu.com/s/1qWBdV0C  介绍:本教程是国内唯一的Swift版的Spritekit教程.本教程基于Xcode 6.1+iOS 8.1开发环境,采用Swift语言,详细讲解Sprite Kit游戏开发的各种知识,帮助读者尽快iOS游戏开发的技能. 目录 目  录 第1章  编写第一个Sprite Kit程序 1 1.1  Sprite Kit介绍 1 1.1.1  什么是Sprit

HealthKit开发教程Swift版:起步

原文:HealthKit Tutorial with Swift: Getting Started 作者:Ernesto García 译者:Mr_cyz ) HealthKit是iOS 8中的新的API,它提供了一种优雅的方式来获取和存储用户的健康数据. 在本篇HealthKit教程中,你将会创建一个简单地记录用户信息的app.在此过程中,你会学到许多关于HealthKit的知识,例如: 怎么样向用户请求允许来获得HealthKit的数据 怎么样读取信息然后将其格式化展示在屏幕上 怎么样将数据

Swift版iOS游戏框架Sprite Kit基础教程下册

Swift版iOS游戏框架Sprite Kit基础教程下册 试读下载地址:http://pan.baidu.com/s/1qWBdV0C 介绍:本教程是国内唯一的Swift版的Spritekit教程.本教程基于Xcode 6.1+iOS 8.1开发环境,采用Swift语言,详细讲解Sprite Kit游戏开发的各种知识,帮助读者尽快iOS游戏开发的技能. 目录 第7章  音频和视频 1 7.1  背景音乐 1 7.1.1  添加背景音乐 1 7.1.2  控制背景音乐 5 7.1.3  设置音乐

关东升的iOS实战系列图书 《iOS实战:入门与提高卷(Swift版)》已经上市

?? 承蒙广大读者的厚爱我的 <iOS实战:入门与提高卷(Swift版)>京东上市了,欢迎广大读者提出宝贵意见.http://item.jd.com/11766718.html 欢迎关注关东升新浪微博@tony_关东升. 关注智捷课堂微信公共平台,了解最新技术文章.图书.教程信息 更多精品iOS.Cocos.移动设计课程请关注智捷课堂官方网站:http://www.zhijieketang.com智捷课堂论坛网站:http://51work6.com/forum.php 版权声明:本文为博主原

关东升的iOS实战系列图书 《iOS实战:传感器卷(Swift版)》已经上市

?? 承蒙广大读者的厚爱我的 <iOS实战:传感器卷(Swift版)>京东上市了,欢迎广大读者提出宝贵意见.http://item.jd.com/11760248.html 欢迎关注关东升新浪微博@tony_关东升. 关注智捷课堂微信公共平台,了解最新技术文章.图书.教程信息 更多精品iOS.Cocos.移动设计课程请关注智捷课堂官方网站:http://www.zhijieketang.com智捷课堂论坛网站:http://51work6.com/forum.php 版权声明:本文为博主原创文

关东升的《iOS实战:图形图像、动画和多媒体卷(Swift版)》上市了

承蒙广大读者的厚爱我的<iOS实战:图形图像.动画和多媒体卷(Swift版)>京东上市了,欢迎广大读者提出宝贵意见..http://item.jd.com/11760249.html 欢迎关注关东升新浪微博@tony_关东升. 关注智捷课堂微信公共平台,了解最新技术文章.图书.教程信息 更多精品iOS.Cocos.移动设计课程请关注智捷课堂官方网站:http://www.zhijieketang.com智捷课堂论坛网站:http://51work6.com/forum.php

代码重构(二):类重构规则(Swift版)

在上篇博客<代码重构(一):函数重构规则(Swift版)>中,详细的介绍了函数的重构规则,其中主要包括:Extract Method, Inline Method, Inline Temp, Replace Temp with Query, Introduce Explaining Variable, Split Temporary Variable, Remove Assignments to Parameters, Replace Method with Method Object等.关于

C# WCF简单入门图文教程(VS2010版)

C# WCF简单入门图文教程(VS2010版) 作者:丶一二丶 字体:[增加 减小] 类型:转载 时间:2016-03-17我要评论 这篇文章主要介绍了WCF简单入门图文教程,版本是VS2010版,帮助大家轻松学习了解DataContract.ServiceContract等特性,感兴趣的小伙伴们可以参考一下 在这个例子中我们将使用VS2010创建一个WCF服务,其中会了解[DataContract][ServiceContract]等特性. 内置的WCFSVCHost,并使用“WCF测试客户端

webservice的Axis2入门教程java版

本文转自百度文库 Axis2是一套崭新的WebService引擎,该版本是对Axis1.x重新设计的产物.Axis2不仅支持SOAP1.1和SOAP1.2,还集成了非常流行的REST WebService,同时还支持Spring.JSON等技术.这些都将在后面的系列教程中讲解.在本文中主要介绍了如何使用Axis2开发一个不需要任何配置文件的WebService,并在客户端使用Java和C#调用这个WebService. 一.Axis2的下载和安装 读者可以从如下的网址下载Axis2的最新版本: