第八章:地图和位置

在这一章节中,你将会学到在你的app中如何使用iPhone中的GPS。你将会学会如何获得用户的位置,同时在地图上标注出位置。本章内容涵盖了 地图套件(Map Kit)和苹果公司提供的地图、方向框架。通过学习本章节的内容,你马上就可以搞定基于位置定位的应用。

提供用户的位置信息是开发iOS应用最激动人心的特性,在地图上显示用户的位需要两个步骤。

第一步用Core Location来收集用户的位置。Core Location是一系列类的集合,通过设备的GPS和蜂窝获取位置信息,还能借助WIFI获取用户信息。Core Location 是由苹果公司提供的众多frameworks中的一个。Frameworks是一组类的集合,为具体的某个任务而设计的一套工具。我们现在用Core Location举一个例子,Core Location是为了处理用户位置信息而设计的一个framework。Core Data是为了处理数据而设计的一个framework。这些framework是可选的,因此需要先把这些框架导入到工程当中后,才能使用这些框架。

第二步是在地图中标注出用户的位置。苹果公司提供了Map Kit框架,帮助我们绘制和管理地图。在Xcode 6中导入框架非常容易。首先Project Navigator中点击项目名称,点击名为Capabilities的tab选项按钮,向下滚动找到Maps。将地图的开关键处于On的状态,这时MapKit框架已经添加到工程中了。现在虽然能够在工程中找到MapKit,但是导入流程还没有完成。MapKit还需要导入controller file中。我们下列一行代码完成导入工作:

import MapKit

这样,MapKit类和协议就导入到controller文件中了。MapKit中的类和协议都是以MK开头的。

Page 213

Core Location

正如之前所说的,Core Location是一系列查找用户位置的类的集合。Core Location中有三个检测用户位置的方法。第一个方法是Significant-Change Location。这个方法能够节约电池电量,它只在用户的位置明显改变时才会更新位置。第二个方法是Location Services,可以自主规定定位更新的规则。最后一种方法是Regional Monitoring方法,使用附近的地理区域边界或者Bluetooth beacons来定位。本书主要介绍第二种方法:Location Services,它是最常用到的方法。

更多信息请参考苹果公司的Location and Maps Programming Guide

获取用户位置需要使用Core Location框架,当你把Maps capabilities开关切换成On(开)状态时,Xcode并没有自动导入Core Location框架,需要我们手动导入,请看以下四个步骤:

  1. 点击Project Navigator上的蓝色工程图标;
  2. Editor显示工程的详细信息,滑倒最下方;
  3. 在Link Binary with Libraries下方点击Add;
  4. 选择Core Location,然后点击Add。

Core Location框架就会添加到Project Navigator中,我们还需要在controller中写一行代码才能获取此框架:

import CoreLocation

手机用户的位置非常耗费电量,它比其他的任务需要更多的电池电量和天线频率,所以确保你的App只有在需要位置时才获取位置,一旦获取到位置,就把这个功能关掉,如果将来还需要地理位置,可以使用定期更新功能

在获取用户位置之前,很重要的一件事是先检查定位服务是否可用。定位服务无法使用可能是由于以下几种情况:

  • 用户在设置中关闭了Location Services(定位服务)。
  • 用户禁止你的App使用Location Services(定位服务)。
  • 设备处于飞行模式或者连接不了网络。

Page 214 | Chapter 8 : Maps and Location

Core Location提供了名为locationServicesEnabled的方法来检查设备的定位服务是否可用,locationServicesEnabled方法通过布尔类型返回值来确定定位服务是否可用,true可用,false不可用。

Requesting User Location

通过CLLocationManager类来请求用户位置。首字母CL代表Core Location。The location manager用于收集参数和开启定位服务。创建CLLocationManager对象和创建其他的对象类似。举例说明:

var locationManager: CLLocationManager = CLLocationManager()

CLLocationManager有一些属性是必须要设置的。

desiredAccuracy属性是枚举类型,枚举,就是用一个关键词代表一个数字。枚举有点像是多选题,你必须从选项中挑选出一个值来。desiredAccuracy属性有下面一些值:

kCLLocationAccuracyBest 最精准的定位,也是最消耗电量的选项 kCLLocationAccuracyNearestTenMeters 精准度在十米范围内 kCLLocationAccuracyHundredMeters 准确度在一百米范围内 kCLLocationAccuracyKilometer 精确度在一千米范围内 kCLLocationAccuracyThreeKilometers 精确度在三千米范围内

精准度越高,电量消耗越大。我们要选择能够满足最低要求的精准度级别。如果是像Google地图之类的App来追踪用户的位置,那么kCLLocationAccuracyNearestTenMeters或者kCLLocationAccuracyHundredMeters就可以满足我们的需求。如果App只需提供用户所在城市,像是Twitter中的定位,kCLLocationAccuracyKilometer或者kCLLocationAccuracyThreeKilometers就可以满足我们的需求。大多数情况下,一般不需要kCLLocationAccuracyBest

设置desiredAccuracy属性的方法和设置其他对象的属性一样:

locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters

Core Location | Page 215

CLLocationManager也需要delegate属性。CLLocationManage delegate遵循CLLocationManagerDelegate协议。无论何时出现了位置更新或者出现错误,delegate都会接收到警告。为了接收这些警告,delegate必须通过CLLocationManagerDelegate协议与警报保持沟通。controller必须声明遵循协议,将CLLocationManagerDelegate添加到类的顶部:

class ViewController: UIViewController, CLLocationManagerDelegate

想要接收定位警高,要使用locationManager(_:,didUpdateLocations:)方法。每当定位信息改变的时,这个方法就会被调用:

func locationManger(manager: CLLocationManager!,didUpdateLocations locations: [AnyObject]!) {
    println("Location found")
}

想要在任何时候都能收到Core Location的错误警告,需要使用locationManager(_: didFailWithError:)方法:

func locationManager(manager: CLLocationManager!, didFailWithError error:NSError!) {
    println("Error!")
}

一旦遵循协议并使用其中的方法后,就必须要设置delegate的属性:

locationManager.delegate = self

在激活定位服务之前, 用户必须同意app使用用户的位置信息。定位服务有两种批准类型。第一种是requestWhenInUseAuthorization;授权App仅限前台运行的时使用位置信息。 第二种是requestAlwaysAuthorization. 授权App在前台还是后台运行都可以获取用户的位置信息,第二个授权都会给app提供追踪用户位置的能力。调用授权的方法是:

locationManager.requestWhenInUseAuthorization()

locationManager.requestAlwaysAuthorization()

获得授权后,调用startUpdatingLocation()方法开启定位服务:

locationManager.startUpdatingLocation()

这样,locationManager会根据相关要求开始追踪并返回用户位置信息。

Page 216 | Chapter 8 : Maps and Location

locationManager (_:, didUpdateLocations:)方法会提供CLLocation数组,按照出现前后顺序排列。数组中至少会有一个对象。数组中的每一个对象都是是一个CLLocation。CLLocation这个类为具体的位置整理组织CLLocationManager的位置数据。CLLocation跟踪地理坐标,海拔,速度,方向,甚至包括定位准确度。CLLocation拥有许多有用的属性:

coordinate CLLocationCoordinate2D, 纬度坐标和经度坐标 altitude 海拔高度,单位:米 timestamp 获取到数据时的时间和日期 description 用字符串的格式返回CLLocation,可以用print()打印出来

请牢记,一旦你获得了你需要的信息,必须停止定位服务功能。为了停止这些服务,在CLLocationManager中调用stopUpdatingLocation() :

manager.stopUpdatingLocation()

举个例子,在locationManager( manager: ,didUpdateLocations:)方法中获取位置后,常常会停止定位服务。之前在CLLocationManager中创建的那些变量非常适合处理目前的这种情况。

明白了! iOS 8模拟器在模拟Core Location时会出现一些前后不一致的行为。如果定位服务没有调用,在Info.plist文件中添加三个键: NSLocationWhenInUsageDescription NSLocationAlwaysUsageDescription NSLocationUsageDescription 每个键对应的值设置成Always或者When in Use 这三个键值会帮助开启定位服务

Core Location | Page 217

Map Kit

Map Kit框架提供地图和方向,地图可以展示到街道级别的信息,3D建筑,卫星图像,或者将两者组合起来。地图自动响应缩小、放大、平移、倾斜等手势动作,还能在地图上标注点同时加上注解。

MKMapView

Map Kit提供MKMapView视图类来展示地图,MKMapView可以展示地图,管理用户的输入信息,展示自定义注释。

MKMapView也有一个delegate属性。和CLLocationManager的delegate属性一样,MKMapView的delegate也能接收updates。MKMapView delegate需要遵循MKMapViewDelegate协议。设置delegate的方法是,从Storyboard的Editor中,将Map View用Control拖动法拖动到Document Outline中的View Controller文字上,然后弹出一个菜单,点击菜单中的delegate,这样就在相关界面上设置好了delegate。

MKMapView有很多方便的属性和方法。举了例子,MKMapView不用添加任何代码就可以在地图上展示用户地理位置。我们把属性设置showsUserLocationtrue,就可以在地图上显示用户信息了:

myMapView.showsUserLocation = true

用户的位置将会在地图上用一个蓝点标注出来。

一般我们把用户所在位置设置为地图的中心点。如果想移动重新设置地图中心点,需要设置centerCoordinate属性,centerCoordinate属性需要CLLocationCoordinate2DCLLocationCoordinate2D是经度和纬度的坐标,被打包成一个单独的变量。通过CLLocationCoordinate2DMake方法创建CLLocationCoordinate2D

var coordinates: CLLocationCoordinate2D = CLLocationCoordinate2DMake(100,100)

有时候我们会在地图上放大位置,当region属性设置好后,放大后图像会自动调整。region属性需要MKCoordinateRegion对象,然而,大部分情况下,比起创建新的对象,编辑当前的region对象会更简单一些:

var updatedRegion: MKCoordinateRegion = myMapView.region
updatedRegion.span.longitudeDelta = updatedRegion.span.longitudeDelta * 2.0
updatedRegion.span.latitudeDelta = updatedRegion.span.latitudeDelta * 2.0 
myMapView.region = updatedRegion

Page 218 | Chapter 8 : Maps and Location

longitudeDeltalatitudeDelta都是span的一部分,span是面积有多大,以centerCoordinate为中心可展示的宽度和高度。

Directions (方向)

Map Kit还能够在App中提供建议规划路线导航功能。MKDirections API可以根据苹果服务器的计算提供线路方向。有步行线路规划,驾驶线路规划,花费的时间,和其他可选的路线。地图上的每个点用MKMapItem表示,MKMapItem包含了地图上有关地点的所有信息,这些信息包括地图位置,坐标值,地点名称等数据。MKMapItem还能传到地图应用上,使用地图应用上更多高级功能。

创建MKMapItem最简单的方法是使用mapItemForCurrentLocation方法,这个方法获取用户的位置然后根据位置创建MKMapItem:

var mapItem: MKMapItem = MKMapItem.mapItemForCurrentLocation()

MKMapItem类有一些便利的属性。name属性是一个字符串,提供地点的描述性名称。phoneNumber属性也是字符串,存储这个位置的电话号码。URL属性存储位置的网址。

MKMapItem创建后,就可以轻松的把位置传递到地图应用上,使用导航功能。openMapWithItems: launchOptions方法可接收一个数组,数组中包括包括一至多个的MKMapItem。通过launchOptions,这么些items就会被映射到地图应用上。MKLaunchOptionsDirectionsModeKey让地图应用基于两个点来提供规划路线。

Plotting Points (绘制点、标注点)

苹果公司提供了一个在地图上绘制点的方法,叫annotations(注解)。annotations是可以定义一个地方或者一个点。它常常用于突出感兴趣的地点,提供更多细节。annotations也拥有一个可选标注气泡(optional callout bubble)。气泡代表一些位置的名字和地址那样的信息。气泡也是可点击的,可以像button(按钮)那样接收用户的动作。

annotations由两部分组成,注解对象(annotation object)和注解视图(annotation view)。annotation object是一个轻量级对象,管理annotation中的数据。annotation object是从MKPointAnnotation类中创建的。annotation view是从MKPinAnnotationView类中创建的。annotation view用来在地图上标注pin(大头针)。

Map Kit | Page 219

三个步将annotation添加到MKMapView中。第一步是为感兴趣的地点创建一个MKPointAnnotation:

var point = MKPointAnnotation()
point.coordinate = CLLocationCoordinate2DMake(37.7756, -122.4193)
point.title = "San Francisco"

接下来,遵循MKMapView协议,回应mapView(_: viewForAnnotation:)方法,此方法可以回收利用annotation view,就像是table view中也有方法可以重复利用cell:

func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!)-> MKAnnotationView! {
    var pin = MKPinAnnotationView(annotation: annotation, reuseIdentifier:"pinIdentifier")
    return pin
}

最后,调用addAnnotation方法,这样,就把annotation添加到地图中了:

mapView.addAnnotation(point)

现在,我们来搞定你第一个使用了地图App吧。