什么创设一个像RunKeeper一样的App必发bifa88手机客服端

什么样创设一个像RunKeeper一样的App(一)swift版

</br>
本博将不定期更新外网的iOS最新教程

简书: @西木

腾讯网: @角落里的monster

本文翻译自raywenderlich,版权归原作者所有,转载请注明出处

原文地址为 http://www.raywenderlich.com/97944/make-app-like-runkeeper-swift-part-1

</br>

原博提供了两份示例代码,分别为刚开始项目配置时的、part 1完成时的,地址分别为:

http://cdn4.raywenderlich.com/wp-content/uploads/2015/05/MoonRunner-Starter.zip

http://cdn4.raywenderlich.com/wp-content/uploads/2015/05/MoonRunner-Part1-Final.zip

</br>
那篇教程将向您出示,如何是好一个像样于RunKeeper一样,基于GPS的可以记下您跑步轨迹的app.

本条新的app.大家就叫它MoonRunner吧.

接下去,你就要完毕那一个动态轨迹记录app的持有机能

  • 追踪主旨地点
  • 当您跑步时显示地图,并且实时更新行动路线
  • 在奔跑时频频显示当前的平均速度
  • 按照距离不一样设置徽章奖励系统
  • 当您这一次跑步甘休的时候显得这一次跑步全部的运转轨迹

Getting Started

请下载本课程对应的代码,路径为:
http://cdn4.raywenderlich.com/wp-content/uploads/2015/05/MoonRunner-Starter.zip

开拓并运行程序就能够瞥见一个极度简短的布局:

  • 首页突显的是3个大致的领航按钮
  • 你能够记录或者起始四遍新的跑步记录的时候所在的百般界面是NewRun界面
  • 在三遍跑步的详情页可以望见本次跑步的详细音讯,包涵彩色标注的地图

Starting the Run

首先,大家必要对项目做一些设置

  • 点击MoonRunner的project navigator
  • 选择Capabilities tab
  • 打开Background Modes
  • 勾选Location Updates

本条设置能够确保,就算你零时须要接听电话,程序推入后台时依旧维持地方音讯的翻新

必发bifa88手机客服端 1

接下去,选用Info tab,打开Custom iOS Target
Properties,将下边两行加入plist

key type value
NSLocationWhenInUseUsageDescription String MoonRunner wants to track your run
NSLocationAlwaysUsageDescription String MoonRunner wants to track your run

它的效应是,iOS会弹出提醒框询问用户是或不是允许该app使用location data

注意
万一您的app要上传App
Store,你必要在App的discription中表明:在后台持续利用GPS会缩减电池的寿命

接下去回去代码中,打开NewRunViewController.swift加入

import CoreLocation
import HealthKit

因为你必要广大的根据地方消息的API和Health模块的method

下一场,你还必要在文件底部参与class
extension,来遵从CLLocationManagerDelegate磋商

// MARK:- CLLocationManagerDelegate
extension NewRunViewController:CLLocationManagerDelegate{

}

跟着你须求完成部分代理方法,达成对于地方新闻更新的监听

接下去参加一些分子属性

var seconds = 0.0
var distance = 0.0

lazy var locationManager:CLLocationManager = {
    var _locationManager = CLLocationManager()

    _locationManager.deledate = self
    _locationManager.desiredAccuracy = kCLLocationAccuracyBest
    _locationManager.activityType = .Fitness

    // Movement threshold for new events
    _locationManager.distanceFilter = 10.0
    return _locationManager
}()

lazy var locations = [CLLocation]()
lazy var timer = NSTimer()

那几个属性的意趣

  • seconds : 记录轨道的事件间隔,单位是秒
  • distance : 甘休当前无时无刻跑了多少距离,单位是米
  • locationManager : 对象,在start或者stop的随时记录用户的任务
  • timer : 记录时刻,更新UI

CLLocationManager和它的布置

当懒加载的时候,你就会为NewRunViewController设置代理CLLocationManagerDelegate

继而设置了精确性为best(_locationManager.desiredAccuracy =
kCLLocationAccuracyBest),在您活动的时候,你可以非凡规范地读取自己的任务音信,同样,你也会成本相比较多的电量

activityType属性设置为.Fitness的用途:
例如当你过马路或者甘休的时候,它会只可以的转移配置崩你节省电量

distanceFilter设置为10米,绝对于desireAccuracy,那几个特性不会潜移默化电量,它是便于展现此外属性的值的

若果您做个小小的测试你就会意识,你的位移音讯不是一条直线而是有不胜枚举锯齿状

高精度的distanceFilter就足以减小锯齿,给你一个更规范地轨道,不过,太高的精度值会让您的轨迹像素化(看到多如牛毛莱比锡克),所以10m是一个对立相比较适中的值

接下去,在viewWillAppear(animated: BOOL)方法的结尾加上这一行

locationManager.requestAlawayAuthorization()

其一措施是iOS8才有的,用来呼吁用户授权允许行使地方新闻,如果您想让您的App兼容iOS8事先的版本,还亟需测试兼容性

接下去在促成中投入那些点子

override func viewWillDisappear(animated: Bool) {
   super.viewWillDisappear(animated)
   timer.invalidate()
}

其一措施的情趣是,当导航控制器不出示该页面时,时间的笔录也会告一段落

再添加这么些法子

func eachSecond(timer:NSTimer) {
        seconds++
        let secondsQuantity = HKQuantity(unit: HKUnit.secondUnit(), doubleValue: seconds)
        timeLabel.text = "Time: " + secondsQuantity.description
        let distanceQuantity = HKQuantity(unit: HKUnit.meterUnit(), doubleValue: distance)
        distanceLabel.text = "Distance: " + distanceQuantity.description

        let paceUnit = HKUnit.secondUnit().unitDividedByUnit(HKUnit.meterUnit())
        let paceQuantity = HKQuantity(unit: paceUnit, doubleValue: seconds / distance)
        paceLabel.text = "Pace: " + paceQuantity.description
    }

本条点子每一秒都会调用,在调用的时候,你持有的数据值都会尾随时间变化而立异

在您从头跑步前呢,还有最终一个方法要调用

func startLocationUpdates() {
        // Here, the location manager will be lazily instantiated
        locationManager.startUpdatingLocation()
    }

以此办法会报告manager,要求起初更新地方音信了

在真的奔跑此前呢,还必要在startPressed(sender:
AnyObject)方法中投入那个代码

    seconds = 0.0
    distance = 0.0
    locations.removeAll(keepCapacity: false)
    timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "eachSecond", userInfo: nil, repeats: true)
    startLocationUpdates()

必发bifa88手机客服端 2

其一界面会没完没了立异具有数据

编译运行,如若你start你就可以看见时间值初步在不停增加

唯独,distance和pace文本框不会更新,因为你还未曾绘制位移轨迹,接下去大家做那有的

Recording the Run

你曾经成立了一个CLLocationManager对象,你应当去那里得到要翻新的多寡,那么些足以因此代理来兑现

仍旧是NewRunViewController.swift那几个文件,给我们事先写的 class extension
达成 CLLocationManagerDelegate 代理方法

func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
        for location in locations as! [CLLocation] {
            if location.horizontalAccuracy < 20 {
                // update distance
                if self.locations.count > 0 {
                    distance += location.distanceFromLocation(self.locations.last)
                }

                //sace location
                self.locations.append(location)
            }
        }
    }

万一有岗位更新的时候这么些格局就会被调用,平时景况下,locations这几个数组唯有一个元素,如果有多个,它们会根据时间先后排序

CLLocation中富含了成百上千音信,包含经纬度等等。可是在读取这一个音讯此前,会有一个horizonAccuracy的审批,要是设备觉得那项数据在20米之内的值不是很准确的时候,会自动的将那项数据从数额汇总移除。这么些审批功效在奔跑的时候极其首要,用户在第三次开行开展校准的时候,那一个时候,它或许会更新一些不精确的数目

一经CLLocation通过了检测,就会起先推断距离。那时候distaFromLocation(_location:
CLLocation)方法就很有益于了,它可以考虑到种种奇怪的关系到地球曲面的情事

必发bifa88手机客服端,说到底,添加那一个岗位对象生成一个一段时间内位暴发的包涵多个任务对象的数组

注意
CLLocation对象也富含了相呼应的VerticalAccuracy的海拔的数值,每一个runner都知道,小山坡会给跑步进度添加很多分裂的感触,因为海拔中度会潜移默化您对氧气的必要量,它会给你有些微细的挑战,当然,这些数目也会引用在App里

Send the Simulator on a run

自己盼望这么些科目和App的怒放能让您对移动与健身爆发极大的热心肠,可是你付出的时候不须要依据字面意思逐字地去了然它

你不须求在测试的时候确实的拿先河机去跑步,模拟器就足以帮你成功那些职责

在模拟器中启动程序,然后选拔Debug ->Location ->City
Run,模拟器就会给你一个虚构的数量

必发bifa88手机客服端 3

自然,那样相比便于也不要求开销精力去测试相对于别的的按照地点新闻的App

然而,我也会提出您真的拿起始机做一个可依赖测试,那样你才有时机去微调你的地点管理的参数,去评估你得到的多少质量

与此同时也助长你养成健康的生活习惯

Saving the Run

你前边曾经筹划好了UI,那么现在安装数据吧

加盟那个办法到NewRunViewController.swift中

func saveRun() {
    // 1
    let savedRun = NSEntityDescription.insertNewObjectForEntityForName("Run",
      inManagedObjectContext: managedObjectContext!) as! Run
    savedRun.distance = distance
    savedRun.duration = seconds
    savedRun.timestamp = NSDate()

    // 2
    var savedLocations = [Location]()
    for location in locations {
      let savedLocation = NSEntityDescription.insertNewObjectForEntityForName("Location",
        inManagedObjectContext: managedObjectContext!) as! Location
      savedLocation.timestamp = location.timestamp
      savedLocation.latitude = location.coordinate.latitude
      savedLocation.longitude = location.coordinate.longitude
      savedLocations.append(savedLocation)
    }

    savedRun.locations = NSOrderedSet(array: savedLocations)
    run = savedRun

    // 3
    var error: NSError?
    let success = managedObjectContext!.save(&error)
    if !success {
      println("Could not save the run!")
    }
  }

此间做了何等吧?如若您前边做了Core Data
Flow的话,那看起来很像是保存了一条新的跑步记录:

1.你创立了一个新的记录,记录了您活动的距离和消费的时辰

2.当把一多元的CLLocation对象保存到一个Location对象里面的时候,你就把跑步经过的这个坐标点都连接起来了

3.保存NSManagedObjectContext

终极,当用户甘休奔跑并且要把这一次跑步的笔录封存下去的时候就会调用那几个方法。找到

extension NewRunViewController: UIActionSheetDelegate {
}

这个extension,在

if buttonIndex == 1

以此block里地第一行写上

saceRun()

编译、运行,你就足以开端一遍新的奔走记录同时把数据保存下来

必发bifa88手机客服端 4

只是,跑步详细音信的界面依旧是空的,现在大家去完毕它

Revealing the Map

当今,须求我们去调出地图,打开 DetailViewController.swift 并且导入
Healthkit

import HralthKit

下一场,将上边的代码写到configView()方法里

func configureView() {
    let distanceQuantity = HKQuantity(unit: HKUnit.meterUnit(), doubleValue: run.distance.doubleValue)
    distanceLabel.text = "Distance: " + distanceQuantity.description

    let dateFormatter = NSDateFormatter()
    dateFormatter.dateStyle = .MediumStyle
    dateLabel.text = dateFormatter.stringFromDate(run.timestamp)

    let secondsQuantity = HKQuantity(unit: HKUnit.secondUnit(), doubleValue: run.duration.doubleValue)
    timeLabel.text = "Time: " + secondsQuantity.description

    let paceUnit = HKUnit.secondUnit().unitDividedByUnit(HKUnit.meterUnit())
    let paceQuantity = HKQuantity(unit: paceUnit, doubleValue: run.duration.doubleValue / run.distance.doubleValue)
    paceLabel.text = "Pace: " + paceQuantity.description

  }

此处安装了跑步的详细新闻,可以突显在显示器的文本框里

1.率先,要安装你所在的地理地点

2.设置了活动轨迹的初阶点

3.装置了快慢的来得风格

将上面的法门参加类中

func mapRegion() -> MKCoordinateRegion {
    let initialLoc = run.locations.firstObject as! Location

    var minLat = initialLoc.latitude.doubleValue
    var minLng = initialLoc.longitude.doubleValue
    var maxLat = minLat
    var maxLng = minLng

    let locations = run.locations.array as! [Location]

    for location in locations {
      minLat = min(minLat, location.latitude.doubleValue)
      minLng = min(minLng, location.longitude.doubleValue)
      maxLat = max(maxLat, location.latitude.doubleValue)
      maxLng = max(maxLng, location.longitude.doubleValue)
    }

    return MKCoordinateRegion(
      center: CLLocationCoordinate2D(latitude: (minLat + maxLat)/2,
        longitude: (minLng + maxLng)/2),
      span: MKCoordinateSpan(latitudeDelta: (maxLat - minLat)*1.1,
        longitudeDelta: (maxLng - minLng)*1.1))
  }

MKCoordinateRegion可以依据你提供的骨干岗位,水平和竖直范围来规定当前显示屏显示的是哪位区域

比如说,当您想让您的移动轨迹的显示看起来比较舒畅女士一点的话,可以拖拽或者缩放地图。这或多或少索要了然地告知用户,那样她见状的门径才能展现在显示屏中央

随后,添加底下那几个措施

func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
    if !overlay.isKindOfClass(MKPolyline) {
      return nil
    }

    let polyline = overlay as! MKPolyline
    let renderer = MKPolylineRenderer(polyline:polyline)
    renderer.strokeColor = UIColor.blackColor()
    renderer.lineWidth = 3
    return renderer
  }

这几个方法表示当代表轨迹的曲线重合的时候,你的轨道曲线的颜色会变本加厉,让您看起来更直观,颜色加深的那部分是由一种类的地点点爆发的

接下去,你须要为polyline定义一个coordinates,添加那些方法

func polyline() -> MKPolyline {
    var coords = [CLLocationCoordinate2D]()

    let locations = run.locations.array as! [Location]
    for location in locations {
      coords.append(CLLocationCoordinate2D(latitude: location.latitude.doubleValue,
        longitude: location.longitude.doubleValue))
    }

    return MKPolyline(coordinates: &coords, count: run.locations.count)
  }

这里您将Location的数码传入到了CLLocationCoordinate2D那一个数组中

下一场,添加以下措施

func loadMap() {
    if run.locations.count > 0 {
      mapView.hidden = false

      // Set the map bounds
      mapView.region = mapRegion()

      // Make the line(s!) on the map
      loadMap()
    } else {
      // No locations were found!
      mapView.hidden = true

      UIAlertView(title: "Error",
        message: "Sorry, this run has no locations saved",
        delegate:nil,
        cancelButtonTitle: "OK").show()
    }
  }

其一法子中,地点点绘制完结,地图展现的区域为开始跑步前安设的区域,重合轨迹的局地做了强化的颜色渲染

末段,将那么些艺术添加到configView()的末段

loadMap()

现在,编译运行,你就可以在模拟器上收看如此的地形图突显了

必发bifa88手机客服端 5

Finding the Right Color

以此App已经很cool了,可是你还足以体现用户跑的究竟有多快,那样,他们就可以辨别在不一致的时局条件中,他们有没有保持在适龄的速率上

要做那一个功效的话,你须要增添polyline这个类

新建一个类,叫做MulticolorPolylineSegment,打开,删除里面的情节,写入以下代码

import UIKit
import MapKit

class MulticolorPolylineSegment: MKPolyline {
  var color: UIColor?
 }

其一自定义的polyline将用来渲染轨迹的每一个有些。颜色的浓淡将意味着速度的进度,如此以外,它和MKPolyline是一模一样的。它们都是用来形容连接两个地方点支架的线条

接下去你要规定,在什么的线条上边使用什么的颜料。添加那一个类格局在MulticolorPolylineSegment
那些类中

private class func allSpeeds(forLocations locations: [Location]) -> (speeds: [Double], minSpeed: Double, maxSpeed: Double) {
    // Make Array of all speeds. Find slowest and fastest
    var speeds = [Double]()
    var minSpeed = DBL_MAX
    var maxSpeed = 0.0

    for i in 1..<locations.count {
      let l1 = locations[i-1]
      let l2 = locations[i]

      let cl1 = CLLocation(latitude: l1.latitude.doubleValue, longitude: l1.longitude.doubleValue)
      let cl2 = CLLocation(latitude: l2.latitude.doubleValue, longitude: l2.longitude.doubleValue)

      let distance = cl2.distanceFromLocation(cl1)
      let time = l2.timestamp.timeIntervalSinceDate(l1.timestamp)
      let speed = distance/time

      minSpeed = min(minSpeed, speed)
      maxSpeed = max(maxSpeed, speed)

      speeds.append(speed)
    }

    return (speeds, minSpeed, maxSpeed)
  }

本条方法会再次来到一个数组,那几个数组装得是不可计数的职位点相对应的速度值,其中也就概括了最大速度和微小速度。重返的多个值,你可以将它们放在一个元组里

第一,你应该注意的是输入的具有地点点是一个环。你需求将每一个Location转换成CLLocation,那里您可以行使
func distanceFromLocation(_ location: CLLocation!) ->
CLLocationDistance 这些方法

基于物文学常识,速度 = 路程 /
时间,所以你就可以赢得用户在跑步中每一时时速度的变通情形

本条方法是私家方法,只好在类里面调用。然后,添加这一个办法

class func colorSegments(forLocations locations: [Location]) -> [MulticolorPolylineSegment] {
    var colorSegments = [MulticolorPolylineSegment]()

    // RGB for Red (slowest)
    let red   = (r: 1.0, g: 20.0 / 255.0, b: 44.0 / 255.0)

    // RGB for Yellow (middle)
    let yellow = (r: 1.0, g: 215.0 / 255.0, b: 0.0)

    // RGB for Green (fastest)
    let green  = (r: 0.0, g: 146.0 / 255.0, b: 78.0 / 255.0)

    let (speeds, minSpeed, maxSpeed) = allSpeeds(forLocations: locations)

    // now knowing the slowest+fastest, we can get mean too
    let meanSpeed = (minSpeed + maxSpeed)/2

    return colorSegments
  }

那边,你定义了两种颜色分别表示慢速、中速、火速。每一种颜色,分别有它的RGB值得范围,最慢得一些是全黄色,最快是全灰色,中速是纯肉色,此外时候颜色会基于速度大小在己酉革命->黄色->肉色时间渐变,所以最后突显出来的结果肯定会很刺眼

必发bifa88手机客服端 6

亟待注意的是您哪些从allspeeds那些元组中获得最大值、最小值和平均值

最终,在刚刚的法子的末端到return colorSegments 此前加入那段代码

for i in 1..<locations.count {
      let l1 = locations[i-1]
      let l2 = locations[i]

      var coords = [CLLocationCoordinate2D]()

      coords.append(CLLocationCoordinate2D(latitude: l1.latitude.doubleValue, longitude: l1.longitude.doubleValue))
      coords.append(CLLocationCoordinate2D(latitude: l2.latitude.doubleValue, longitude: l2.longitude.doubleValue))

      let speed = speeds[i-1]
      var color = UIColor.blackColor()

      if speed < minSpeed { // Between Red & Yellow
        let ratio = (speed - minSpeed) / (meanSpeed - minSpeed)
        let r = CGFloat(red.r + ratio * (yellow.r - red.r))
        let g = CGFloat(red.g + ratio * (yellow.g - red.g))
        let b = CGFloat(red.r + ratio * (yellow.r - red.r))
        color = UIColor(red: r, green: g, blue: b, alpha: 1)
      }
      else { // Between Yellow & Green
        let ratio = (speed - meanSpeed) / (maxSpeed - meanSpeed)
        let r = CGFloat(yellow.r + ratio * (green.r - yellow.r))
        let g = CGFloat(yellow.g + ratio * (green.g - yellow.g))
        let b = CGFloat(yellow.b + ratio * (green.b - yellow.b))
        color = UIColor(red: r, green: g, blue: b, alpha: 1)
      }

      let segment = MulticolorPolylineSegment(coordinates: &coords, count: coords.count)
      segment.color = color
      colorSegments.append(segment)
    }

在此地,你可以得到先行总括的进程值、速度的界定,也就足以由速度变化的速度程度来规定颜色变化的深浅程度

接下去,你可以依照七个像对应的坐标和颜料创制一个新的MulticolorPolylineSegment。最终,你收集到拥有的颜料部分后,就足以准备上马渲染了

Applying the Multicolored Segments

想要让detail View Controller使用新的 multicolor polyline
很粗略,打开DetailViewController.swift,找到 loadMap() 方法,将

mapView.addOverlay(polyline())

替换成

let colorSegments = MulticolorPolylineSegment.colorSegments(forLocations: run.locations.array as! [Location])

mapView.addOverlays(colorSegments)

那边开创了一个segments的数组,并且把持有overlays加到了map上

末尾,你要准备给polyline下边每个segment渲染成特定的颜色,所以,用上边的代码重写你的mapView方法

func mapView(mapView:MKMapView!, rendererForOverlay Overlay:MKOverlay!)->NKOverlayRenderer!{
    if !overlay.isKindOfClass(MulticolorPolylineSegment) {
      return nil
    }

    let polyline = overlay as! MulticolorPolylineSegment
    let renderer = MKPolylineRenderer(polyline: polyline)
    renderer.strokeColor = polyline.color
    renderer.lineWidth = 3
    return renderer
}

看起来和事先的很像,不过现在,每一个segment都被渲染成了特定的颜料

重新编译运行,你就能见到这么一个色彩丰裕的地形图突显了

必发bifa88手机客服端 7

Leaving a Trail Of Breadcrumbs

说到底生成的地图看起来很灿烂,不过在奔跑进程中它是哪些的呢

打开Main.storyboard 找到New Run Scene,拖一个MapKit View进来到“Ready to
launch” label和start button之间

必发bifa88手机客服端 8

下一场,为它助长约束

  • 顶部离开label 20 point
  • 地步距离button 20 point
  • 反正相距superView都为0

下一场打开 NewRunViewController.swift 添加

import MapKit

跟着,添加成员属性

@IBOutlet weak var mapView:MKMapView!

在 viewWillAppear 方法中添加

mapView.hidden = true

使地图开头时处于hidden状态,在startPressed 方法末尾添加

mapView.hidden = false

点击start的时候地图现身

在文书末尾添加 class extension 完毕代理方法

// MARK: - MKMapViewDelegate
extension NewRunViewController: MKMapViewDelegate {
  func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
    if !overlay.isKindOfClass(MKPolyline) {
      return nil
    }

    let polyline = overlay as! MKPolyline
    let renderer = MKPolylineRenderer(polyline: polyline)
    renderer.strokeColor = UIColor.blueColor()
    renderer.lineWidth = 3
    return renderer
  }
}

此间和 run details screen 里地很像,可是此地的stroke color仍旧是紫色的

接下去,你必要写代码去创新地图的来得区域,并且在每暴发一个得力地Location的时候描绘轨迹,将你的locationManager(_:didUpdateLocations:)方法的落实立异成

func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
    for location in locations as! [CLLocation] {
      let howRecent = location.timestamp.timeIntervalSinceNow

      if abs(howRecent) < 10 && location.horizontalAccuracy < 20 {
        //update distance
        if self.locations.count > 0 {
          distance += location.distanceFromLocation(self.locations.last)

          var coords = [CLLocationCoordinate2D]()
          coords.append(self.locations.last!.coordinate)
          coords.append(location.coordinate)

          let region = MKCoordinateRegionMakeWithDistance(location.coordinate, 500, 500)
          mapView.setRegion(region, animated: true)

          mapView.addOverlay(MKPolyline(coordinates: &coords, count: coords.count))
        }

        //save location
        self.locations.append(location)
      }
    }
  }

现今,你眼前的岗位一向在地图的最基本,同时,黄色的活动轨迹随着你的位移在不断延长

打开Main.storyboard找到NewRunScene,连接mapView 到map
View,并且安装代理为目前控制器

编译运行,将会见到地图实时更新

必发bifa88手机客服端 9

Where To Go From Here

那边有这一个事例的完全代码
http://cdn4.raywenderlich.com/wp-content/uploads/2015/05/MoonRunner-Part1-Final.zip

你可以看看哪些用Core
Data存储数据,怎么着在地图上显示详细的奔跑新闻,那是其一App最宗旨的一部分

假诺你的技术相比好的话,你可以试试怎么着使用海拔中度音讯,怎么着改变轨道宽度,如何使用一小段的平均速度使颜色变化比此前更为通畅

in any
case,那篇教程还会有第二有的,为您介绍为每个用户定制的徽章奖励体制

注:

1.本文翻译自
http://www.raywenderlich.com/97944/make-app-like-runkeeper-swift-part-1

2.原博part 2 已履新,着急的同班可事先查看
http://www.raywenderlich.com/97945/make-app-like-runkeeper-swift-part-2

  • 不定期送上iOS最新教程,但力量简单,翻译不准的地点还望指正

简书:@西木

搜狐:@角落里的monster

相关文章