怎创建一个如RunKeeper一样的App(一)swift版

By admin in 亚洲必赢app在哪下载 on 2018年10月24日

如何创建一个诸如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

是装置可以保,即使你零时需要接听电话,程序推入后台时仍然保持位置信息之翻新

亚洲必赢app在哪下载 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()

亚洲必赢app在哪下载 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)方法就那个有益了,它会考虑到各种奇异的涉到地曲面的情状

末了,添加这个位置对象好成一个一段时间内位产生的带有多个职务对象的数组

注意
CLLocation对象为蕴含了彼此对应之VerticalAccuracy的海拔的数值,每一个runner都知,小山坡会叫跑步过程添加很多无一样的感受,因为海拔高度会潜移默化你对氧的需求量,它见面吃你有的小的挑战,当然,这个数目吧会见引用在App里

Send the Simulator on a run

自己希望此课程以及App的绽开能够叫你针对走及健身产生巨大的热心肠,但是若开之当儿不需要按字面意思逐字地去了解她

而莫待以测试的上的确的以在手机去跑,模拟器就可协助您做到这个职责

于模拟器中启动程序,然后择Debug ->Location ->City
Run,模拟器就见面于您一个虚构的多寡

亚洲必赢app在哪下载 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()

编译、运行,你就可初步同软新的跑记录同时将数量保存下去

亚洲必赢app在哪下载 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()

今天,编译运行,你虽足以以模拟器上看这样的地图显示了

亚洲必赢app在哪下载 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值得范围,最缓慢得有凡清一色红,最抢是清一色绿色,中速是纯黄色,其它时候颜色会依据速度大小在红色->黄色->绿色时渐变,所以最后显示出的结果一定会老刺眼

亚洲必赢app在哪下载 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都吃渲染成了特定的水彩

重复编译运行,你就是会看出如此一个色彩丰富的地形图显示了

亚洲必赢app在哪下载 7

Leaving a Trail Of Breadcrumbs

最终生成的地形图看起颇灿烂,但是当跑步过程被它是怎的也罢

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

亚洲必赢app在哪下载 8

接下来,为它助长约束

  • 顶部相差label 20 point
  • 地步距离button 20 亚洲必赢app在哪下载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,并且安装代理为当下控制器

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

亚洲必赢app在哪下载 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

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2018 亚洲必赢app官方下载 版权所有