传奇传奇怎么添加新地图图,运行游戏后,@移动 地图名 ,提示地图号不存在!求解! 地图添加过程,1.用地图编

Google地图SDK入门教程(Swift版)
招聘信息:
原文:&本文由CocoaChina和共同翻译,译者:slowwind在iOS中使用地图为开发者提供了无线的可能性,程序书籍中通常用整个章节来说明在iOS中如何使用地图。从仅仅在地图显示某个位,到绘制多个中间位置组成一条旅行路线,或者甚至可以用完全不同的方式来探索地图的可能性,处理这些事情毫无疑问是一次极好的经历,并能得到让人惊叹的成果。到iOS 5.1(并包含此版本)为止,iOS使用Google Mobile Maps服务来提供对地图的访问以及所有的相关服务。从那之后,事情发生了变化,Apple推出了Map Kit,这是一款全新的完全内置于iOS中的框架,并被使用至今。从Apple停止使用Google的地图服务时,Google便决定创造自己的、适用于包括iOS在内所有平台的地图SDK,从而与Map kit或其他平台的其他地图SDK竞争。目前,许多开发者都使用此SDK,Google在此领域则成为了强者。因此为iOS上的Google Maps SDK写点东西绝对是值得的。在写作此入门教程之时,iOS的Google Maps SDK版本是1.9.2。它包括许多特性,其中绝大多数包含在地图的web版本中,而另一方面,则也有许多特性在移动平台上缺失而无法使用。值得一提的是,此版本SDK所占硬盘空间较大,而如果你要拷贝框架源代码到你的工程,这就成了必须考虑的事情。然而,它提供的特性是十分有趣和重要的,我想都没想就拷贝了。与我相反,这次我的介绍不会很长。这主要因为我们在接下来的部分有许多事情要做,而在这里开始讨论不同特性是完全漫无目标的,因此我们会在之后详细地审视它们。我想说的是,准备好迎接那些真正有趣的东西吧,如果你过去从未使用过 Google Maps SDK,那么你肯定会喜欢上它的。在接下来的章节里我会全面阐述开发者在使用地图过程中所要处理的最常见任务。简而言之,下面是我们将要学习的:如何在地图上显示用户当前位置如何定位自定义地址如何绘制路径如何在路径中添加中间点(路径点)更多…因此,为不浪费更多时间,让我们将话题转移到所有今天将会遇到的好东西吧。Demo概览介绍下教程中使用的Demo,首先无须从头开始创建新工程,相反你可以用一个作为基础,然后我们向其中添加新功能。所以要先下载它,并在Xcode中打开,做好准备。此示例程序实际是一个单视图程序,并包含三个子视图:屏幕顶部边沿的工具栏,包含一些工具栏按钮。在初始工程中,这些工具栏按钮项已经与IBAction方法连接。占据了大部分可用屏幕区域的视图(UIView),这里将显示地图。底部的标签,在这里我们将显示路线的长度和时耗。接下来,顶部工具栏的工具栏按钮项从左到右如下:指定自定义地址设置路径的起始和结束位置改变行进模式改变地图类型在接下来的教程部分中,我们会看到以上所有这些。而目前,你所要做的是快速浏览起始工程,从而做到在其中能轻松翻阅。下面你见到的是一组屏幕截图,作为本教程我们要达到的目标的一个样例。重要提示:如果你下载和测试最终的应用,确认设置了你的API key到AppDelegate.swift文件。阅读下一部分来获得更多有关信息。获取API Key使用Google Maps SDK前,首先需要获取一个API Key。这意味着我们需要从Google获取一段特殊的字符串,以便稍后从app中调用Google API。此API Key需要从Google Developers Console取得,正如此名称所暗示的,这是跟开发者有关的一个"地方"。显然,你必须要有Google账号,如果你还没有账号,先去创建一个。另一种情况,如果你有Google账号,那么可以顺利地往下继续。为获取在应用中使用的API Key,你可以忽略这里给出的指示而遵循Google给出的。但是,在此教程写作之时,Google Developers Console的接口并不与以上指南相符(显然此接口更新了,而指南还没有更新),因此我建议你按照如下步骤来获取你的API Key。那么,我们开始吧。使用你的Google账号,登录并点击如下屏幕显示的API Project选项:然后,点击展开API & auth目录,并选择API选项,这样就找到Google提供的所有可用API,但我们感兴趣的只是iOS的Google Maps SDK。根据浏览器的不同,API要么显示成列表,必须滚动它直到找到地图API;要么显示成组,这样你只需要点击合适的组即可。以下两个屏幕截图说明了这两个选项:选择iOS的Google Maps SDK API,进入新页面,在这里你要做的是使能此API:完此步,点击Credentials选项,又一次来到API & auth目录之下。在新的页面里,点击Create new Key按钮,此按钮位于左下角:点击此按钮,弹出一个对话框窗口,询问你要创建的Key的类型。显然,你必须点击iOS Key按钮,从而为我们的程序产生一个合法的Key。在下一个对话框窗口中你必须输入或粘贴应用的bundle identifier(bundle ID)。起始工程的bundle ID值为com.appcoda.GMapsDemo,只需拷贝并粘贴它,如下所示:上一步是必需的,从而使我们的应用被授权使用Google Maps API。注意,如果你计划在更多的应用程序中使用相同的API Key,那么你首先必须在此对话框(当然在此之后你能找到它)添加它们的bundle IDs。点击Create按钮,则所需的API Key便生成了。在你返回的窗口的右下角,你会看到一块与下图相似的区域:如上面所示,点击Edit allowed iOS applications可以增加或移除应用的bundle IDs。你也能重新生成此键值,但目前这不是必需的。你所需要做的仅是选择此API Key并拷贝它,这样你就可以在之后粘贴它到工程中。工程配置当使用非iOS提供的SDK时有一个缺点,这是因为工程总依赖于将要使用的SDK,需要作某种程度的配置。因此,在此部分我们要做一些必需的初始化步骤来使用Google Maps SDK,否则就不可能使用它。注意以下描述的一些步骤能在Google的官方文档中找到。而其他一些步骤并没有在官方文档中描述,你必须搜索一下才能查到它们。当然,在本文档中你无需自行搜索了,步骤都在这里了。首先,你需要下载Google Maps SDK(此框架必须添加到工程中)。你可从此处;点击Download the SDK按钮,并在你跳转到的页面中选择建议的版本(目前为1.9.2)。一旦你获取到它 ,解压并往下继续。如果你还没有打开起始工程,那么在Xcode中打开它。然后从Finder拖动GoogleMaps.framework到Project Navigator。当Xcode询问你时,请保证选择Copy items if needed选项,并且当然不要忘记勾选GMapsDemo:接下来,返回到Finder,并再次点击GoogleMaps.framework。从Resources文件夹中选择GoogleMaps.bundle并把它拖入到Xcode的Project Navigator中。将此bundle添加到工程的时候,与以上描述作相同的选择。进行到此处,下面两项应该出现在你的工程里(Google Maps SDK 组是我为方便更好地组织文件而创建的):为使Google Maps SDK正常工作,需要包含几个其他的框架到工程中。在我给你必须添加的框架和静态库列表之前,请保证在Project Navigator中选择工程,点击Build Phases并展开Link Binary With Libraries选项。使用加号(+)按钮来按下面的列表来逐项增加:AVFoundation.frameworkCoreData.frameworkCoreLocation.frameworkCoreText.frameworkGLKit.frameworkImageIO.frameworklibc++.dyliblibicucore.dyliblibz.dylibOpenGLES.frameworkQuartzCore.frameworkSystemConfiguration.framework这儿是你最终应该看到的:接下来,点击Build Setting标签页,并寻找Other Linker Flags 设置。找到之后,将-ObjC赋值给它。这是因为我们要将Objective-C编写的地图SDK链接到目前的Swift工程。说到链接,不幸的是还没有真正直接的方式在Swift中嵌入Objective-C的代码。而在我们的情况中,我们需要这么做,因为我们必须导入Google地图静态库的Objective-C头文件。实际上,我们需要的是Xcode为我们创建一个Objective-C头文件(.h),但没有直接的方法可以做到,我们将间接地按如下步骤解决此问题:1.在键盘上敲击Command + N键,在工程中增加一个新文件。2.在文件模板中,选择Objective-C File选项。按照向导进行,命名文件为temp并创建它。3.完成向导后,Xcode会显示如下信息,务必点击Yes按钮。4.除了temp.m,也将创建一个命名为 GMapsDemo-Bridging-Header.h的新文件。选择它在编辑器中打开,并按添加如下代码行:import5.删除temp.m文件,我们并不需要它。最后,是来使用我们之前小节中生成的API Key的时候了。打开AppDelegate.swift文件,并在application(_:didFinishLaunchingWithOptions:) 函数中增加如下代码行:func&application(application:&UIApplication,&didFinishLaunchingWithOptions&launchOptions:&[NSObject:&AnyObject]?)&->&Bool&{
&&&&//&Override&point&for&customization&after&application&launch.
&&&&GMSServices.provideAPIKey("YOUR_API_KEY")
&&&&return&true
}当然,你必须将"YOUR_API_KEY"字符串替换成你在Google Developers Console那里生成的真正API Key。值得注意的是,如果没有按前述步骤操作,比如没有桥接和导入头文件,那么就不可能访问GoogleMaps框架和它的类(就像上述代码片段所示)。工程的初始化准备现在完成了,我们从现在开始可以投入到编码中了。如同我在此部分开始所说,此类配置在使用外部SDK时是无法避免的,但值得庆幸的是,在这种情况下,所有的前期工作即便繁多,却都是很容易的。显示地图现在工程的所有必需初始化准备都完成了,让我们来开始做一些真正的编程工作,将Google地图第一次添加到应用中。就如你接下来将要看到的,这十分简单,按合理顺序来完成。如果你打开Main.storyboard文件,你会注意到在View Controller场景中有一个视图(UIView)位于画布正中央。此视图将作为我们的地图视图,但为了按此目的使用它,我们需要对它做一些修改。因此,首先选中它,然后打开Utilities面板。找到Identity Inspector,在类的选项区域中将类名设置为GMSMapsView。此GMSMapsView是Google Maps框架的一部分,实际上是UIView的子类。在此视图中还有一处修改需要完成,但这次我们必须到ViewController.swift文件中。在类的顶部你可以找到如下的IBOutlet属性声明:@IBOutlet&weak&var&viewMap:&UIView!在这里我们所需要做的是改变viewMap属性的类,如下所示,从UIView变为GMSMapView:@IBOutlet&weak&var&viewMap:&GMSMapView!现在,我们可将viewMap作为我们的地图视图了。将Google地图添加到应用中真的十分简单。只需到viewDidLoad方法中增加如下两行:override&func&viewDidLoad()&{
&&&&super.viewDidLoad()
&&&&//&Do&any&additional&setup&after&loading&the&view,&typically&from&a&nib.
&&&&let&camera:&GMSCameraPosition&=&GMSCameraPosition.cameraWithLatitude(48.857165,&longitude:&2.354613,&zoom:&8.0)
&&&&viewMap.camera&=&camera
}设置地图时的第一项任务是初始化一个GMSCameraPosition对象。利用此对象,我们指定将在地图上显示的初始位置。上图的坐标以巴黎地图为中心,但你可以找到其他的坐标来替代(这里只是一个演示)。除了坐标之外,我们还需要指定初始的地图缩放级别。以上对象被赋值给地图视图的camera属性。这样,实际地图可以成功的在viewMap视图中渲染。现在你可以第一次测试此应用了。因此,为测试它而使用真实的设备或模拟器,并等待地图出现在视图上吧。在我们进入下一部分前,稍微扩展一下我们所做的东西。让我们创建一个动作表单(action sheet),使我们可以选择另一种地图类型。Maps SDK在iOS中提供三种类型:普通地形混合普通模式是默认模式,它如上图所示。其他两个选项根据各自的类型改变了地图的外观,如果我们能将它们设置到我们的地图中,那可就太好了。地图视图包含了一个命名为mapType的属性。在Google Maps SDK中有三个常量来表示每种类型,此属性必须设置成其中一个。这些常量是:kGMSTypeNormalkGMSTypeTerrainkGMSTypeHybrid现在,我们到hangeMapType(_:) IBAction方法中,在这里我们将增加必要代码来显示action sheet并为地图视图设置合适的地图类型。下面代码片段包含所有你需要的:@IBAction&func&changeMapType(sender:&AnyObject)&{
&&&&let&actionSheet&=&UIAlertController(title:&"Map&Types",&message:&"Select&map&type:",&preferredStyle:&UIAlertControllerStyle.ActionSheet)
&&&&let&normalMapTypeAction&=&UIAlertAction(title:&"Normal",&style:&UIAlertActionStyle.Default)&{&(alertAction)&->&Void&in
&&&&&&&&self.viewMap.mapType&=&kGMSTypeNormal
&&&&let&terrainMapTypeAction&=&UIAlertAction(title:&"Terrain",&style:&UIAlertActionStyle.Default)&{&(alertAction)&->&Void&in
&&&&&&&&self.viewMap.mapType&=&kGMSTypeTerrain
&&&&let&hybridMapTypeAction&=&UIAlertAction(title:&"Hybrid",&style:&UIAlertActionStyle.Default)&{&(alertAction)&->&Void&in
&&&&&&&&self.viewMap.mapType&=&kGMSTypeHybrid
&&&&let&cancelAction&=&UIAlertAction(title:&"Close",&style:&UIAlertActionStyle.Cancel)&{&(alertAction)&->&Void&in
&&&&actionSheet.addAction(normalMapTypeAction)
&&&&actionSheet.addAction(terrainMapTypeAction)
&&&&actionSheet.addAction(hybridMapTypeAction)
&&&&actionSheet.addAction(cancelAction)
&&&&presentViewController(actionSheet,&animated:&true,&completion:&nil)
}以上的实现中没有什么特别困难的。如你看到的,在每个action中(除了cancel action)我们指定了地图视图的合适地图类型。现在再次运行此应用。这次你仅需使用顶部工具栏的最右边的工具栏按钮项,就可以简单地改变地图类型。我的位置在屏幕上显示地图绝对是有趣的,但是如果它没有指向对象,那就没什么用处。因此,从此部分开始,我们将逐步添加新功能使得我们的地图真正实用。当使用地图时,最通常的任务是指出用户的当前位置。这做起来很简单,但需要用户的同意才能获取到当前位置。从iOS 8开始,向用户申请权限分为两步:首先,必须在工程的.plist文件中添加一个新条目。此条目以所需权限的类型作为关键字,以描述信息作为值,此描述信息会在应用首次运行时在一个警告视图中呈现给用户。第二步是以编程的方式请求权限,如果没有显示警告给用户,这将会触发警告控制器的出现。我们待会儿就将看到。当使用Core Location服务时(我们在这会使用得很多),应用会监控位置更新,要么一直监控(即使应用在后台运行),要么仅当应用被使用时才监控。上述提到的那种权限就是这些事情;取决于我们需要应用如何去监控位置变化,我们在.plist文件中提供相应的关键字,然后我们在代码里请求相应的权限。在我们的情况中没有让应用始终监控位置更新的需求。在应用没有运行时,如果地图没有任何的改变发生,监控位置更新是没有任何意义的,而且对设备的电池也是极大的浪费。在Project Navigator中定位并打开info.plist文件。然后,到Editor > Add Item目录增加一个新的条目。以字符串NSLocationWhenInUseUsageDescription作为关键字,并填写任意你喜欢的描述作为值。可选择使用如下为:"允许访问你的位置,你可以在地图上定位你的位置。"(不包括引号部分)。现在回到ViewController.swift文件,让我们声明两个接下来我们马上要用到的两个属性。在类的顶部,增加如下两行,紧跟在IBOutlet属性声明之后:3var&locationManager&=&CLLocationManager()
var&didFindMyLocation&=&falselocationManager属性会被用于向用户请求权限来追踪他的位置,并基于授权状态来确定显不显示他的当前位置。didFindMyLocation 标识在之后会被用到,从而我们可知道用户的当前位置是否在地图上被定位了,并最终避免不必要的位置更新。但是,先做重要的事情。如上图的代码片段所示,locationManager对象在声明时就被初始化了。然而,这是不够的。在viewDidLoad方法中,我们必须设置ViewController类作为它的代理,并向用户请求权限。如下:override&func&viewDidLoad()&{
&&&&locationManager.delegate&=&self
&&&&locationManager.requestWhenInUseAuthorization()
}要注意的是,在此处的请求类型必须匹配我们在.plist文件中增加的请求类型。通过调用位置管理器对象的requestWhenInUseAuthorization()方法,要么应用第一次运行,会显示一个警告视图给用户,向他请求权限来跟踪他的当前位置,要么系统会返回他之前指定的偏好。在任何情况下,我们都必须通过CLLocationManagerDelegate协议的特定的代理方法来检查应用的授权状态,但在此之前,让我们继承此协议。找到类的头文件中类所在行,增加协议声明:class&ViewController:&UIViewController,&CLLocationManagerDelegate&{
}现在让我们看看代理方法:func&locationManager(manager:&CLLocationManager!,&didChangeAuthorizationStatus&status:&CLAuthorizationStatus)&{
&&&&if&status&==&CLAuthorizationStatus.AuthorizedWhenInUse&{
&&&&&&&&viewMap.myLocationEnabled&=&true
}在此时我们意识到,我们感兴趣的唯一情形是授权状态与上图所示的值相匹配的时刻。在此种情况下,我们只需设置地图视图的myLocationEnabled标识为true,Maps SDK就会去完成寻找用户当前位置的艰苦工作。用户的当前位置被描述为名为myLocation的地图视图对象属性。关于此属性的好消息是,它是KVO兼容的(键-值 观察兼容的),意味着我们可以简单地通过它的值来观察变化,这样当用户位置更新时我们就能够知道了。如果你想要知道更多关于KVO机制的,你可以看一下此。因此,基于以上所述,我们下一步是让我们的类观察到viewMap对象中myLocation属性的变化。再次回到viewDidLoad方法中,增加下述代码行:override&func&viewDidLoad()&{
&&&&viewMap.addObserver(self,&forKeyPath:&"myLocation",&options:&NSKeyValueObservingOptions.New,&context:&nil)
}当Google Maps SDK定位到用户的当前位置时,我们真正想得到的是,此位置位于地图中央,且显示为众所周知的蓝点。事实上此蓝点会自动显示,因此我们无须为此作额外编码。因此,只要用户的位置被定位,我们就把此当前位置显示在地图上,如果我们还没这么做,那么添加如下代码:override&func&observeValueForKeyPath(keyPath:&String,&ofObject&object:&AnyObject,&change:&[NSObject&:&AnyObject],&context:&UnsafeMutablePointer)&{
&&&&if&!didFindMyLocation&{
&&&&&&&&let&myLocation:&CLLocation&=&change[NSKeyValueChangeNewKey]&as&CLLocation
&&&&&&&&viewMap.camera&=&GMSCameraPosition.cameraWithTarget(myLocation.coordinate,&zoom:&10.0)
&&&&&&&&viewMap.settings.myLocationButton&=&true
&&&&&&&&didFindMyLocation&=&true
}如同我所说过的,didFindMyLocation标识能帮助应用来避免不必要的位置更新。更进一步的,我们从变更字典中可得到地图中的新位置。此字典由系统以参数形式传递给上述方法,我们使用NSKeyValueChangeNewKey关键字能获取到我们观察的属性改变后的新值。想要使用此位置信息,我们新建了一个GMSCameraPosition对象,并将它直接赋值给地图视图的camera属性。这会使摄像机(Camera)以新位置为中心。在这里,另一个有趣的任务是通过设置地图视图的属性设置中的myLocationButton属性为true,我们设法在屏幕的右下角显示了一个可返回当前位置的默认按钮,以免用户在地图上左右移动时无法返回当前位置。此按钮由Google Maps SDK提供。也有一个指南针按钮可以同样的方式使能,但在这儿我们并不需要它。现在你可以再试一试此应用。如果你在模拟器中测试它,你必须为它提供一个假位置,因为你的真实位置不会在模拟器中监测到。要做到这一点,只需简单编辑工程的scheme,在Option标签页中选择一个默认位置:定位自定义位置比找到当前用户位置更进一步的,是另一个使用地图时非常通用的场景--定位自定义位置。通常通过提供给地图API某种地址(比如街道名,城市,国家,区域),而不是坐标(经纬度)值,来完成此项功能的,因为没人能记住这些坐标值。但是你有没有真正好奇过,地图服务不知道坐标值,怎么可能定位到你提供的确切地址呢?答案是--有一个名为geocoding的进程,在地图定位之前,通过此进程将地址翻译成经纬度值。甚至于,地图服务(例如Google Maps)根本不关心我们提供的地址,因为它将地址"翻译"为地理坐标。因此,真正有趣之处在于我们如何设法向Google Maps SDK给出地址,而此地址又如何转换成地理坐标,并且又怎样显示到我们的地图上。值得庆幸的是,Google提供了Google Geocoding API,一项web服务,简而言之,接受包含真实地址的请求,并返回经度和纬度值(当然是不同于地址的其他值)作为响应。如果你真的对使用Google地图感兴趣,那么你绝对需要好好读读。实际上,我鼓励你现在就访问此文档并快速浏览服务细节。它会帮你更好的理解我们下一步将要做什么。geocoding API的响应数据要么使用JSON格式,要么使用XML格式。在此示例中,我们将使用JSON格式,因为它仅使用一行代码即可转换成Swift(或Objective-C)的数据表示(字典或数组)。如下的示例从Google文档中直接截取过来,并且是JSON响应的一个极好例子:{
&&&"results"&:&[
&&&&&&&&&"address_components"&:&[
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&"long_name"&:&"1600",
&&&&&&&&&&&&&&&"short_name"&:&"1600",
&&&&&&&&&&&&&&&"types"&:&[&"street_number"&]
&&&&&&&&&&&&},
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&"long_name"&:&"Amphitheatre&Pkwy",
&&&&&&&&&&&&&&&"short_name"&:&"Amphitheatre&Pkwy",
&&&&&&&&&&&&&&&"types"&:&[&"route"&]
&&&&&&&&&&&&},
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&"long_name"&:&"Mountain&View",
&&&&&&&&&&&&&&&"short_name"&:&"Mountain&View",
&&&&&&&&&&&&&&&"types"&:&[&"locality",&"political"&]
&&&&&&&&&&&&},
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&"long_name"&:&"Santa&Clara",
&&&&&&&&&&&&&&&"short_name"&:&"Santa&Clara",
&&&&&&&&&&&&&&&"types"&:&[&"administrative_area_level_2",&"political"&]
&&&&&&&&&&&&},
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&"long_name"&:&"California",
&&&&&&&&&&&&&&&"short_name"&:&"CA",
&&&&&&&&&&&&&&&"types"&:&[&"administrative_area_level_1",&"political"&]
&&&&&&&&&&&&},
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&"long_name"&:&"United&States",
&&&&&&&&&&&&&&&"short_name"&:&"US",
&&&&&&&&&&&&&&&"types"&:&[&"country",&"political"&]
&&&&&&&&&&&&},
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&"long_name"&:&"94043",
&&&&&&&&&&&&&&&"short_name"&:&"94043",
&&&&&&&&&&&&&&&"types"&:&[&"postal_code"&]
&&&&&&&&&&&&}
&&&&&&&&&],
&&&&&&&&&"formatted_address"&:&"1600&Amphitheatre&Pkwy,&Mountain&View,&CA&94043,&USA",
&&&&&&&&&"geometry"&:&{
&&&&&&&&&&&&"location"&:&{
&&&&&&&&&&&&&&&"lat"&:&37.,
&&&&&&&&&&&&&&&"lng"&:&-122.
&&&&&&&&&&&&},
&&&&&&&&&&&&"location_type"&:&"ROOFTOP",
&&&&&&&&&&&&"viewport"&:&{
&&&&&&&&&&&&&&&"northeast"&:&{
&&&&&&&&&&&&&&&&&&"lat"&:&37.49,
&&&&&&&&&&&&&&&&&&"lng"&:&-122.5
&&&&&&&&&&&&&&&},
&&&&&&&&&&&&&&&"southwest"&:&{
&&&&&&&&&&&&&&&&&&"lat"&:&37.50,
&&&&&&&&&&&&&&&&&&"lng"&:&-122.5
&&&&&&&&&&&&&&&}
&&&&&&&&&&&&}
&&&&&&&&&},
&&&&&&&&&"types"&:&[&"street_address"&]
&&&"status"&:&"OK"
}如果你完整看下来,此响应以两种方式表示了找到的地址:作为地址组件(一个数组,以字典作为数组项),和作为格式化地址,此处全部地址作为单个字符串给出。除此之外,在以上例子中有两点更有趣的地方:包含了经度和纬度值的地理字典,和显式的描述了向geocoding的web服务作出请求的结果状态。从以上所述之中,我们将会在应用中用到三种东西:格式化地址,地理详情和状态。我建议你看一看Google文档中的状态码;在这里讨论它已经超出了此教程的范围。在你往下进行之前,请确保你能明白地"读"懂JSON的数据表示。如果你对JSON感到不适应,那么你应该要到这两个链接看看:和。我想提到的是以"某关键字"开始的是字典,而以中括号([)开始的是数组。现在回到我们的应用,我们将要创建一个新类来向geocoding API提出请求和处理从其返回的响应。我这里谈论新类的有两个理由:此代码能够更多的被复用。之后我们会使用到另一个Google web服务(API),把此类任务集中在一处是十分良好的习惯。因此,创建一个新类,并确保它是NSObject的子类。将它命名为MapTasks并加入到工程中,此时你可继续往下进行了。让我们在MapTasks.swift文件中开始工作吧,先声明一些我们接下来要用到的属性:let&baseURLGeocode&=&"/maps/api/geocode/json?"
var&lookupAddressResults:&Dictionary!
var&fetchedFormattedAddress:&String!
var&fetchedAddressLongitude:&Double!
var&fetchedAddressLatitude:&Double!baseURLGeocode是我们向geocoding发请求时所使用的URL。当然,我们将会把地址作为参数添加到它之上,但由于地址是一个动态值,需要以编程的方式来实现。在lookupAddressResults字典中我们存储从结果中返回的第一个地址。值得注意的是,发给geocoding一个地址,它有可能返回多个结果,但为了简单起见,我们仅保留第一个结果。最后,接下来的三个属性中我们保存的值如其名字所示。在此我们必须为类增加一个初始化函数,从而我们可以在之后的ViewController类中创建此类的对象。由于没有自定义初始化方法的必要,让我们使用默认的吧:override&init()&{
&&&super.init()
}现在,让我们创建如下所示的新方法:3func&geocodeAddress(address:&String!,&withCompletionHandler&completionHandler:&((status:&String,&success:&Bool)&->&Void))&{
}第一个参数是我们想要在地图上定位的地址。第二个参数是一个完成句柄,一旦我们接收并处理完响应数据,此句柄即被调用。此应用仅在此完成句柄被此方法调用后才会将结果显示到地图上。正如你所看到的,此完成句柄有两个参数:第一个是我们从响应中提取的状态字符串,而第二个由我们来指示geocoding是否成功了(意味着我们是否已收到地图所需数据)。现在让我们一步步实现此方法。首先,我们需要正确的组成URL字符串,将它转换为合适的格式,然后用它来创建一个NSURL对象。注意我们先确保了给出的地址是合法的:if&let&lookupAddress&=&address&{
&&&&var&geocodeURLString&=&baseURLGeocode&+&"address="&+&lookupAddress
&&&&geocodeURLString&=&geocodeURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
&&&&let&geocodeURL&=&NSURL(string:&geocodeURLString)
}接下来,我们必须向geocoding API发出请求,并将返回的结果储存到NSData对象中。这儿有个重要的细节,那就是我们以异步方式完成上述过程(包括对所有接收数据的处理),从而使应用在数据获取期间仍是可响应的。接下来你也可以看到,从API获取到数据之后,我们将此数据从JSON格式转换成字典对象:dispatch_async(dispatch_get_main_queue(),&{&()&->&Void&in
&&&&let&geocodingResultsData&=&NSData(contentsOfURL:&geocodeURL!)
&&&&var&error:&NSError?
&&&&let&dictionary:&Dictionary&=&NSJSONSerialization.JSONObjectWithData(geocodingResultsData!,&options:&NSJSONReadingOptions.MutableContainers,&error:&&error)&as&Dictionary&&&&
})上图代码片段中最后一行即是将数据从JSON转换为Swift表示方式。如你所注意到的,我们使用NSError对象来"捕获"在转换过程中可能发生的任何错误。因此,我们现在最关心的是,转换之后此错误值是否是nil。如果不是,那我们会调用完成句柄,并设置标识参数为false:if&(error&!=&nil)&{
&&&&println(error)
&&&&completionHandler(status:&"",&success:&false)
}如果一切进行得顺利,那么我们就会先得到响应的状态。如果它包含"OK"的值,我们就"提取"第一个结果并将其保存到lookupAddressResults字典中,然后我们获取所有其他我们感兴趣的值(格式化地址,经度和纬度)。如果状态是其他值而不是"OK",那么我们将调用完成句柄,并传递状态值给它,但我们又将设置标识为false:else&{
&&&&//&Get&the&response&status.
&&&&let&status&=&dictionary["status"]&as&String
&&&&if&status&==&"OK"&{
&&&&&&&&let&allResults&=&dictionary["results"]&as&Array&&&&&&&&self.lookupAddressResults&=&allResults[0]
&&&&&&&&//&Keep&the&most&important&values.
&&&&&&&&self.fetchedFormattedAddress&=&self.lookupAddressResults["formatted_address"]&as&String
&&&&&&&&let&geometry&=&self.lookupAddressResults["geometry"]&as&Dictionary&&&&&&&&self.fetchedAddressLongitude&=&((geometry["location"]&as&Dictionary)["lng"]&as&NSNumber).doubleValue
&&&&&&&&self.fetchedAddressLatitude&=&((geometry["location"]&as&Dictionary)["lat"]&as&NSNumber).doubleValue
&&&&&&&&completionHandler(status:&status,&success:&true)
&&&&else&{
&&&&&&&&completionHandler(status:&status,&success:&false)
}以上实际是方法的"核心"。值得注意的是,以上使用到的关键字名字是从之前我向你给出的JSON数据例子中而来的,你也可以从Google Geocoding API文档中找到它们。最后,还有一种情况我们需要处理;作为参数传递给方法的地址是否是nil:else&{
&&&&completionHandler(status:&"No&valid&address.",&success:&false)
}这次我们在状态参数中提供了一个自定义的消息。这是完整的方法,放在了同一片段中:func&geocodeAddress(address:&String!,&withCompletionHandler&completionHandler:&((status:&String,&success:&Bool)&->&Void))&{
&&&&if&let&lookupAddress&=&address&{
&&&&&&&&var&geocodeURLString&=&baseURLGeocode&+&"address="&+&lookupAddress
&&&&&&&&geocodeURLString&=&geocodeURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
&&&&&&&&let&geocodeURL&=&NSURL(string:&geocodeURLString)
&&&&&&&&dispatch_async(dispatch_get_main_queue(),&{&()&->&Void&in
&&&&&&&&&&&&let&geocodingResultsData&=&NSData(contentsOfURL:&geocodeURL!)
&&&&&&&&&&&&var&error:&NSError?
&&&&&&&&&&&&let&dictionary:&Dictionary&=&NSJSONSerialization.JSONObjectWithData(geocodingResultsData!,&options:&NSJSONReadingOptions.MutableContainers,&error:&&error)&as&Dictionary&&&&&&&&&&&&if&(error&!=&nil)&{
&&&&&&&&&&&&&&&&println(error)
&&&&&&&&&&&&&&&&completionHandler(status:&"",&success:&false)
&&&&&&&&&&&&}
&&&&&&&&&&&&else&{
&&&&&&&&&&&&&&&&//&Get&the&response&status.
&&&&&&&&&&&&&&&&let&status&=&dictionary["status"]&as&String
&&&&&&&&&&&&&&&&if&status&==&"OK"&{
&&&&&&&&&&&&&&&&&&&&let&allResults&=&dictionary["results"]&as&Array&&&&&&&&&&&&&&&&&&&&self.lookupAddressResults&=&allResults[0]
&&&&&&&&&&&&&&&&&&&&//&Keep&the&most&important&values.
&&&&&&&&&&&&&&&&&&&&self.fetchedFormattedAddress&=&self.lookupAddressResults["formatted_address"]&as&String
&&&&&&&&&&&&&&&&&&&&let&geometry&=&self.lookupAddressResults["geometry"]&as&Dictionary&&&&&&&&&&&&&&&&&&&&self.fetchedAddressLongitude&=&((geometry["location"]&as&Dictionary)["lng"]&as&NSNumber).doubleValue
&&&&&&&&&&&&&&&&&&&&self.fetchedAddressLatitude&=&((geometry["location"]&as&Dictionary)["lat"]&as&NSNumber).doubleValue
&&&&&&&&&&&&&&&&&&&&completionHandler(status:&status,&success:&true)
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&else&{
&&&&&&&&&&&&&&&&&&&&completionHandler(status:&status,&success:&false)
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&}
&&&&&&&&})
&&&&else&{
&&&&&&&&completionHandler(status:&"No&valid&address.",&success:&false)
}是时候使用以上方法了,因此让我们返回到ViewController.swift文件。首先,让我们声明和初始化一个MapTasks类的对象:var&mapTasks&=&MapTasks()现在,我们通过导航找到findAddress(_:) IBAction方法。在工具栏的左边的按钮被按下时,此方法被调用。在此方法内,我们会创建一个带文本输入框的警告控制器,向用户请求地址输入。我们将提供两个动作按钮,一个用来启动地址搜索,一个用来取消控制器。当第一个按钮按下时,我们会调用之前创建的方法,然后我们将处理完成句柄的结果。让我们看看代码实现:@IBAction&func&findAddress(sender:&AnyObject)&{
&&&&let&addressAlert&=&UIAlertController(title:&"Address&Finder",&message:&"Type&the&address&you&want&to&find:",&preferredStyle:&UIAlertControllerStyle.Alert)
&&&&addressAlert.addTextFieldWithConfigurationHandler&{&(textField)&->&Void&in
&&&&&&&&textField.placeholder&=&"Address?"
&&&&let&findAction&=&UIAlertAction(title:&"Find&Address",&style:&UIAlertActionStyle.Default)&{&(alertAction)&->&Void&in
&&&&&&&&let&address&=&(addressAlert.textFields![0]&as&UITextField).text&as&String
&&&&&&&&self.mapTasks.geocodeAddress(address,&withCompletionHandler:&{&(status,&success)&->&Void&in
&&&&&&&&})
&&&&let&closeAction&=&UIAlertAction(title:&"Close",&style:&UIAlertActionStyle.Cancel)&{&(alertAction)&->&Void&in
&&&&addressAlert.addAction(findAction)
&&&&addressAlert.addAction(closeAction)
&&&&presentViewController(addressAlert,&animated:&true,&completion:&nil)
}在完成处理函数的函数体中,我们必须处理这些结果。如果成功标识是false,那么我们将在控制台程序上显示此状态。更多的,如果状态的值等于"ZERO_RESULTS",我们将显示另一个警告控制器,告诉用户此地址没有找到(参见Google文档中的状态码)。另一方面,如果一切正常,我们将使地图以新位置为中心:@IBAction&func&findAddress(sender:&AnyObject)&{
&&&&...&&&&&&&&
&&&&&&&&self.mapTasks.geocodeAddress(address,&withCompletionHandler:&{&(status,&success)&->&Void&in
&&&&&&&&&&&&if&!success&{
&&&&&&&&&&&&&&&&println(status)
&&&&&&&&&&&&&&&&if&status&==&"ZERO_RESULTS"&{
&&&&&&&&&&&&&&&&&&&&self.showAlertWithMessage("The&location&could&not&be&found.")
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&}
&&&&&&&&&&&&else&{
&&&&&&&&&&&&&&&&let&coordinate&=&CLLocationCoordinate2D(latitude:&self.mapTasks.fetchedAddressLatitude,&longitude:&self.mapTasks.fetchedAddressLongitude)
&&&&&&&&&&&&&&&&self.viewMap.camera&=&GMSCameraPosition.cameraWithTarget(coordinate,&zoom:&14.0)
&&&&&&&&&&&&}
&&&&&&&&})&&&&&&&&
}showAlertWithMessage(_:)是另一个自定义方法,仅用于以给定的消息来显示一个警告控制器。它由明确的可复用的代码组成:func&showAlertWithMessage(message:&String)&{
&&&&let&alertController&=&UIAlertController(title:&"GMapsDemo",&message:&message,&preferredStyle:&UIAlertControllerStyle.Alert)
&&&&let&closeAction&=&UIAlertAction(title:&"Close",&style:&UIAlertActionStyle.Cancel)&{&(alertAction)&->&Void&in
&&&&alertController.addAction(closeAction)
&&&&presentViewController(alertController,&animated:&true,&completion:&nil)
}我们至此准备好了。再次测试此应用,并且这次输入地址,此地址将指向地图上某处。如果你正确的输入地址,那么地图会以此地址为中心。添加标记在地图上添加一个标记是一项十分简单的工作,因为仅需两行代码即可完成。然而,有多种属性可以配置,从而使得标记能根据你的需求或偏好来进行定制。在Google Maps SDK中,标记是GMSMarker类的对象。当初始化此对象时,你必须指定以CLLocationCoordinate2D类型表示的经度和纬度。在之前章节中我们创建了这样的一个对象,如下代码行:let&coordinate&=&CLLocationCoordinate2D(latitude:&self.mapTasks.fetchedAddressLatitude,&longitude:&self.mapTasks.fetchedAddressLongitude)只要标记对象被初始化了,地图SDK就知道在哪里放置它,从而为标记对象指定一个地图对象就是必要的了,此标记会添加到此地图上。让我们来看看这在代码里如何完成的,但首先让我们先声明一个标记属性,从而我们可以持有对它的强引用。到ViewController类的开始处并添加如下代码:var&locationMarker:&GMSMarker!现在让我们来定义以下方法。我们会在每次自定义地址定位到地图时调用它,因此我们将位置指定给它:func&setupLocationMarker(coordinate:&CLLocationCoordinate2D)&{
}所需的坐标对象会以参数的形式提供给此方法。让我们初始化此标记:func&setuplocationMarker(coordinate:&CLLocationCoordinate2D)&{
&&&&&locationMarker&=&GMSMarker(position:&coordinate)
&&&&&locationMarker.map&=&viewMap
}让我们返回到findAddress(_:) IBAction方法并再加一行代码,从而使得当找到自定义的位置,并且地图调整到以此位置为中心时,调用上图中的方法。@IBAction&func&findAddress(sender:&AnyObject)&{
&&&&...&&&&&&&&
&&&&&&&&self.mapTasks.geocodeAddress(address,&withCompletionHandler:&{&(status,&success)&->&Void&in
&&&&&&&&&&&&...
&&&&&&&&&&&&else&{
&&&&&&&&&&&&&&&&let&coordinate&=&CLLocationCoordinate2D(latitude:&self.mapTasks.fetchedAddressLatitude,&longitude:&self.mapTasks.fetchedAddressLongitude)
&&&&&&&&&&&&&&&&self.viewMap.camera&=&GMSCameraPosition.cameraWithTarget(coordinate,&zoom:&14.0)
&&&&&&&&&&&&&&&&self.setupLocationMarker(coordinate)
&&&&&&&&&&&&}
&&&&&&&&})&&&&&&&&
}再次运行此应用,并输入自定义地址。现在,一个标记会正好指向此新位置。如同我在此部分开头所说的,标记有多种属性可用来实现某种程度的定制。例如,你可以指定标记的颜色,当接触标记时显示某些文字,或者甚至于改变标记的不透明度。让我们通过配置其中一些属性的方式来扩展上述方法:func&setuplocationMarker(coordinate:&CLLocationCoordinate2D)&{
&&&&locationMarker.title&=&mapTasks.fetchedFormattedAddress
&&&&locationMarker.appearAnimation&=&kGMSMarkerAnimationPop
&&&&locationMarker.icon&=&GMSMarker.markerImageWithColor(UIColor.blueColor())
&&&&locationMarker.opacity&=&0.75
}通过使用title属性,当标记被触碰时,从mapTasks对象获取的格式化地址会被显示在标记之上。appearAnimation指定标记是否以动画方式来显示,而我们赋值给它的仅仅是地图SDK提供的选项。关于标记的颜色,如你所看到的,它不能直接被改变;相反,我们需要访问它的icon属性并以图像形式提供所需颜色,然而接下来全是由GMSMarker类来处理的;我们不用去关心这些。最后,我们也改变了标记的不透明度。以上代码行会导致有点透明的蓝色标记动态地显示在地图上。当转动地图或通过手势改变摄影机位置时,标记仍在原位。然而此行为是有可能改变的,通过指示来告诉SDK你希望标记是平的,从而允许标记根据执行的手势而移动。除此之外,在触碰标记时弹出的气泡框中添加额外文字也是可能的。此额外文字必须赋值给标记的snippet属性。这里是以上提到的两个功能的代码实现:func&setuplocationMarker(coordinate:&CLLocationCoordinate2D)&{
&&&&locationMarker.flat&=&true
&&&&locationMarker.snippet&=&"The&best&place&on&earth."
}如果你再次运行应用,你可以看到所有修改的属性产生的效果。在我们进入下一部分之前,还有一个细节我必须要说明。如我之前提到的,我们在以上方法中建立的标记,会在每次新的自定义地址被找到时添加到地图上。然而,此过程不能正常工作,除非我们先移除它(如果已经存在),然后再添加它。因此,我们需要做的是到此方法的开始处,增加以下条件:func&setuplocationMarker(coordinate:&CLLocationCoordinate2D)&{
&&&&if&locationMarker&!=&nil&{
&&&&&&&&locationMarker.map&=&nil
}以上展现的属性并不是Google Maps SDK中的全部属性。我鼓励你去看看官方文档。你肯定会找到有用的东西来读一下的。绘制路线除了Geocode API之外,Google也提供了其他web服务能够与Maps SDK进行组合。其中之一是Directions API,它能够帮助我们在地图上创建路线。路线实际上是连接两个地点的线段,而且除了起始和目的节点外,它也可以包含中间位置,这些中间位置也称为航路点。我们向Directions API发出请求的方法和向Geocode API发出请求的方式十分相似。这意味着我们必须为web服务提供所需的参数值,并在之后处理返回的数据,此数据可能是JSON或XML格式。我想现在是查看的最好时机,而且特别是这一节,在这节中你能找到JSON响应的一个十分好的例子。在我们进行实现之前,理解关于路线的一些基本情况,以及从API返回给我们的数据是非常重要的。在这里我仅强调某一些方面,但你总能参考官方文档中的更多信息。那么首先,从Directions API返回的单个响应可向我们给出从A点到B点的多条可能路线(在此示例应用中,我们仅使用返回的第一条路线,忽略掉所有剩下的路线)。每条路线由多条路程组成,在Directions API响应的描述中,每条路程只是整条旅程的一部分。现在,每条路程由多个步程组成,在这里步程是一个方向单元,即指示着有关路线应该遵循的方向。步程中含有关于自身的有用数据,比如经过的距离,旅程在此步程中的耗时,此步程的开始和结束位置,在地图上绘制步程所需的点。还有更多的东西,但是我有意仅提及这些,因此你在我们继续往下讲时能把它们记在脑中。比路程和步程更进一步的,路线也包含了其他有用的数据。其中的一块是Google文档网站上给出的响应样例中标记为"概况折线(overview_polyline)"的数据,它包含了地图绘制路线所需要的所有点(polyline,折线,是在地图上创建一条路径所需的线段的集合)。在此演示应用中我们将使用此数据,但我必须预先提醒你,此线路的概况是不准确的;它是真正路线的一种近似。对于包含少数路程的相对短的距离,它似乎能很好的工作,然而有些情况下,基于此概况折线的点而绘制的路线漏掉了一些转弯或道路。实际上,路程和步程越多,此概况折线越不准确。此问题的解决方案是,循环访问和获取到每条路程中每条步程的折线点,然后基于这些点一段段的绘制路线。然而,我们不会在这里应用此方案,因为这会让我们得到一个非常复杂的应用,而我们必须保持此应用的简单,让一切都容易理解。我们将坚持使用概况折线,因此你可以看到它是如何使用的,然后你就能够对你自己的代码做一些修改和改进。现在让我们写一些代码吧,转到MapTasks.swift文件。首先,我们必须声明如下的类属性:let&baseURLDirections&=&"/maps/api/directions/json?"
var&selectedRoute:&Dictionary!
var&overviewPolyline:&Dictionary!
var&originCoordinate:&CLLocationCoordinate2D!
var&destinationCoordinate:&CLLocationCoordinate2D!
var&originAddress:&String!
var&destinationAddress:&String!让我们来看看以上属性都是用来什么的:在selectedRoute中我们会存储从Directions API返回的第一条路线(从全部数量的路线之中)。它是一个字典,包含了其他的字典和数组。在overviewPolyline属性中我们会持有概况折线的字典,此字典中存放了另一个字典,其中包含了需要绘制的线段的所有点。originCoordinate和destinationCoordinate都是CLLocationCoordinate2D对象,相应地表示了起始位置和目的位置的经纬度。originAddress和destinationAddress将会以字符串形式持有起始地址和目的地址,并包含在API响应中。以上全部声明完之后,我们能继续定义以下方法:func&getDirections(origin:&String!,&destination:&String!,&waypoints:&Array!,&travelMode:&AnyObject!,&completionHandler:&((status:&String,&success:&Bool)&->&Void))&{
}除了起始和目的位置(以地址表示)之外,以上方法也接受以下两个参数,航路点(路线中的中间点)数组以及地图上将绘制的旅程的行进模式。使用行进模式,我们能够定义以何种方式来作方向指引,驾车,步行还是骑自行车。最后的参数是(已知的)完成处理器,当我们在地图上使用了数据时会调用它。目前我们不用理会航路点和行进模式,我们将只使用起始和目的地址。如我之前所说,至少在开始的时候,你会看到一些与Geocode API相似的东西。因此,首先我们必须使用此两个地址组成请求的URL字符串,然后将它转换成NSURL对象:func&getDirections(origin:&String!,&destination:&String!,&waypoints:&Array!,&travelMode:&AnyObject!,&completionHandler:&((status:&String,&success:&Bool)&->&Void))&{
&&&&if&let&originLocation&=&origin&{
&&&&&&&&if&let&destinationLocation&=&destination&{
&&&&&&&&&&&&var&directionsURLString&=&baseURLDirections&+&"origin="&+&originLocation&+&"&destination="&+&destinationLocation&
&&&&&&&&&&&&directionsURLString&=&directionsURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!&&&&&&&&&&&&
&&&&&&&&&&&&let&directionsURL&=&NSURL(string:&directionsURLString)
&&&&&&&&else&{
&&&&&&&&&&&&completionHandler(status:&"Destination&is&nil.",&success:&false)
&&&&else&{
&&&&&&&&completionHandler(status:&"Origin&is&nil",&success:&false)
}值得注意的是,在起始或目的地址是nil的情况下,我们在相应的else分支中使用了完成处理器,指示获取方向指引失败,并在同时传递了自定义的消息。下一步骤是使用上述我们刚创建的URL向Directions API请求方向指引。如我们所说,返回的数据会是JSON格式,并且我们所有的工作都会是异步发生的。在下面的代码片段中,首先我们取得JSON数据,然后我们将它转换成字典。我们也处理了可能发生的错误:dispatch_async(dispatch_get_main_queue(),&{&()&->&Void&in
&&&&let&directionsData&=&NSData(contentsOfURL:&directionsURL!)
&&&&var&error:&NSError?
&&&&let&dictionary:&Dictionary&=&NSJSONSerialization.JSONObjectWithData(directionsData!,&options:&NSJSONReadingOptions.MutableContainers,&error:&&error)&as&Dictionary&&&&if&(error&!=&nil)&{
&&&&&&&&println(error)
&&&&&&&&completionHandler(status:&"",&success:&false)
&&&&else&{
})以上所述并没有新东西或困难之处。有趣的部分是我们在else分支添加的代码如下:let&status&=&dictionary["status"]&as&String
if&status&==&"OK"&{
&&&&self.selectedRoute&=&(dictionary["routes"]&as&Array)[0]
&&&&self.overviewPolyline&=&self.selectedRoute["overview_polyline"]&as&Dictionary&&&&let&legs&=&self.selectedRoute["legs"]&as&Array&&&&let&startLocationDictionary&=&legs[0]["start_location"]&as&Dictionary&&&&self.originCoordinate&=&CLLocationCoordinate2DMake(startLocationDictionary["lat"]&as&Double,&startLocationDictionary["lng"]&as&Double)
&&&&let&endLocationDictionary&=&legs[legs.count&-&1]["end_location"]&as&Dictionary&&&&self.destinationCoordinate&=&CLLocationCoordinate2DMake(endLocationDictionary["lat"]&as&Double,&endLocationDictionary["lng"]&as&Double)
&&&&self.originAddress&=&legs[0]["start_address"]&as&String
&&&&self.destinationAddress&=&legs[legs.count&-&1]["end_address"]&as&String
&&&&self.calculateTotalDistanceAndDuration()
&&&&completionHandler(status:&status,&success:&true)
&&&&completionHandler(status:&status,&success:&false)
}让我们先通讲一下代码。首先,我们获得响应的状态,并且我们仅当它是"OK"的值时才往下继续。在此情况下,我们所做的第一件事情是让selectedRoute字典持有第一条找到的路线(为了入门教程的简单起见,如果有更多路线则都被忽略了)。使用此字典,我们直接访问折线概况,因此我们获得了绘制路线所需的点(即使是近似的)。然后,在局部声明的路程数组里,我们存储了路线的所有路程,因为我们将需要它们,从而获得关于开始和结束位置的一些数据。值得注意的是,我们如何访问起始和目的地的坐标,并且当它们从响应中返回时我们如何获取它们的名字。注意,对于开始位置我们使用了路线的第一条路程,而对于结束位置我们使用了路线的最后一条路程(legs.count - 1)。就在我们调用完成处理器之前,我们调用了一个自定义方法,你在这儿还是第一次见到此方法,calculateTotalDistanceAndDuration()。我们会稍后再见到它,而你可以假定它能帮助我们计算旅程的距离和行进的时间。在此,我向你给出此方法的实现:func&getDirections(origin:&String!,&destination:&String!,&waypoints:&Array!,&travelMode:&AnyObject!,&completionHandler:&((status:&String,&success:&Bool)&->&Void))&{
&&&&if&let&originLocation&=&origin&{
&&&&&&&&if&let&destinationLocation&=&destination&{
&&&&&&&&&&&&var&directionsURLString&=&baseURLDirections&+&"origin="&+&originLocation&+&"&destination="&+&destinationLocation
&&&&&&&&&&&&directionsURLString&=&directionsURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
&&&&&&&&&&&&let&directionsURL&=&NSURL(string:&directionsURLString)
&&&&&&&&&&&&dispatch_async(dispatch_get_main_queue(),&{&()&->&Void&in
&&&&&&&&&&&&&&&&let&directionsData&=&NSData(contentsOfURL:&directionsURL!)
&&&&&&&&&&&&&&&&var&error:&NSError?
&&&&&&&&&&&&&&&&let&dictionary:&Dictionary&=&NSJSONSerialization.JSONObjectWithData(directionsData!,&options:&NSJSONReadingOptions.MutableContainers,&error:&&error)&as&Dictionary&&&&&&&&&&&&&&&&if&(error&!=&nil)&{
&&&&&&&&&&&&&&&&&&&&println(error)
&&&&&&&&&&&&&&&&&&&&completionHandler(status:&"",&success:&false)
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&else&{
&&&&&&&&&&&&&&&&&&&&let&status&=&dictionary["status"]&as&String
&&&&&&&&&&&&&&&&&&&&if&status&==&"OK"&{
&&&&&&&&&&&&&&&&&&&&&&&&self.selectedRoute&=&(dictionary["routes"]&as&Array)[0]
&&&&&&&&&&&&&&&&&&&&&&&&self.overviewPolyline&=&self.selectedRoute["overview_polyline"]&as&Dictionary&&&&&&&&&&&&&&&&&&&&&&&&let&legs&=&self.selectedRoute["legs"]&as&Array&&&&&&&&&&&&&&&&&&&&&&&&let&startLocationDictionary&=&legs[0]["start_location"]&as&Dictionary&&&&&&&&&&&&&&&&&&&&&&&&self.originCoordinate&=&CLLocationCoordinate2DMake(startLocationDictionary["lat"]&as&Double,&startLocationDictionary["lng"]&as&Double)
&&&&&&&&&&&&&&&&&&&&&&&&let&endLocationDictionary&=&legs[legs.count&-&1]["end_location"]&as&Dictionary&&&&&&&&&&&&&&&&&&&&&&&&self.destinationCoordinate&=&CLLocationCoordinate2DMake(endLocationDictionary["lat"]&as&Double,&endLocationDictionary["lng"]&as&Double)
&&&&&&&&&&&&&&&&&&&&&&&&self.originAddress&=&legs[0]["start_address"]&as&String
&&&&&&&&&&&&&&&&&&&&&&&&self.destinationAddress&=&legs[legs.count&-&1]["end_address"]&as&String
&&&&&&&&&&&&&&&&&&&&&&&&self.calculateTotalDistanceAndDuration()&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&completionHandler(status:&status,&success:&true)
&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&&&&&else&{
&&&&&&&&&&&&&&&&&&&&&&&&completionHandler(status:&status,&success:&false)
&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&})
&&&&&&&&else&{
&&&&&&&&&&&&completionHandler(status:&"Destination&is&nil.",&success:&false)
&&&&else&{
&&&&&&&&completionHandler(status:&"Origin&is&nil",&success:&false)
}现在,就在我们实现此新的自定义方法来计算距离和时间之前,我们需要在类的顶部声明如下属性:var&totalDistanceInMeters:&UInt&=&0
var&totalDistance:&String!
var&totalDurationInSeconds:&UInt&=&0
var&totalDuration:&String!让我们现在实现此新方法。不幸的是,没有计算路线的距离和时间的直接方法,相反的,我们必须遍历所有路程,并且一段段的把它们加起来。距离是以米来计算的,而时间是以秒来计算的。在下面的实现中你会注意到,在我们计算了相关的数据和之后,我们将距离转换为以千米为单位,而时间转换为为天,小时,分钟和秒的形式。如下:func&calculateTotalDistanceAndDuration()&{
&&&&let&legs&=&self.selectedRoute["legs"]&as&Array&&&&totalDistanceInMeters&=&0
&&&&totalDurationInSeconds&=&0
&&&&for&leg&in&legs&{
&&&&&&&&totalDistanceInMeters&+=&(leg["distance"]&as&Dictionary)["value"]&as&UInt
&&&&&&&&totalDurationInSeconds&+=&(leg["duration"]&as&Dictionary)["value"]&as&UInt
&&&&let&distanceInKilometers:&Double&=&Double(totalDistanceInMeters&/&1000)
&&&&totalDistance&=&"Total&Distance:&\(distanceInKilometers)&Km"
&&&&let&mins&=&totalDurationInSeconds&/&60
&&&&let&hours&=&mins&/&60
&&&&let&days&=&hours&/&24
&&&&let&remainingHours&=&hours&%&24
&&&&let&remainingMins&=&mins&%&60
&&&&let&remainingSecs&=&totalDurationInSeconds&%&60
&&&&totalDuration&=&"Duration:&\(days)&d,&\(remainingHours)&h,&\(remainingMins)&mins,&\(remainingSecs)&secs"
}让我们回到ViewController类,并且首先让我们声明如下属性:var&originMarker:&GMSMarker!
var&destinationMarker:&GMSMarker!
var&routePolyline:&GMSPolyline!开始的两个标记将指示起始和目的位置,并且routePolyline是表示路线的折线。现在在createRoute(_:) IBAction中,我们会显示一个带有两个文本输入框的警告控制器。用户在第一个里输入起始位置,在第二个里输入目的位置。通过接受这两个值来创建路线,我们将调用Directions API,并基于会返回的结果,我们将设计此路线且添加标记。@IBAction&func&createRoute(sender:&AnyObject)&{
&&&&let&addressAlert&=&UIAlertController(title:&"Create&Route",&message:&"Connect&locations&with&a&route:",&preferredStyle:&UIAlertControllerStyle.Alert)
&&&&addressAlert.addTextFieldWithConfigurationHandler&{&(textField)&->&Void&in
&&&&&&&&textField.placeholder&=&"Origin?"
&&&&addressAlert.addTextFieldWithConfigurationHandler&{&(textField)&->&Void&in
&&&&&&&&textField.placeholder&=&"Destination?"
&&&&let&createRouteAction&=&UIAlertAction(title:&"Create&Route",&style:&UIAlertActionStyle.Default)&{&(alertAction)&->&Void&in
&&&&&&&&let&origin&=&(addressAlert.textFields![0]&as&UITextField).text&as&String
&&&&&&&&let&destination&=&(addressAlert.textFields![1]&as&UITextField).text&as&String
&&&&&&&&self.mapTasks.getDirections(origin,&destination:&destination,&waypoints:&nil,&travelMode:&nil,&completionHandler:&{&(status,&success)&->&Void&in
&&&&&&&&&&&&if&success&{
&&&&&&&&&&&&&&&&self.configureMapAndMarkersForRoute()
&&&&&&&&&&&&&&&&self.drawRoute()
&&&&&&&&&&&&&&&&self.displayRouteInfo()
&&&&&&&&&&&&}
&&&&&&&&&&&&else&{
&&&&&&&&&&&&&&&&println(status)
&&&&&&&&&&&&}
&&&&&&&&})
&&&&let&closeAction&=&UIAlertAction(title:&"Close",&style:&UIAlertActionStyle.Cancel)&{&(alertAction)&->&Void&in
&&&&addressAlert.addAction(createRouteAction)
&&&&addressAlert.addAction(closeAction)
&&&&presentViewController(addressAlert,&animated:&true,&completion:&nil)
}正如你注意到的,如果此方向指引请求成功了,我们会调用三个新的自定义方法。它们的用途从名字易见,因此让我们一个个地实现它们。在configureMapAndMarkersForRoute()方法中,我们将完成三项不同的任务:我们将地图调整到以路线的开始点为中心。我们在起始点增加一个标记。我们在目的点增加一个标记。func configureMapAndMarkersForRoute() {viewMap.camera = GMSCameraPosition.cameraWithTarget(mapTasks.originCoordinate, zoom: 9.0)originMarker&=&GMSMarker(position:&self.mapTasks.originCoordinate)
originMarker.map&=&self.viewMap
originMarker.icon&=&GMSMarker.markerImageWithColor(UIColor.greenColor())
originMarker.title&=&self.mapTasks.originAddress
destinationMarker&=&GMSMarker(position:&self.mapTasks.destinationCoordinate)
destinationMarker.map&=&self.viewMap
destinationMarker.icon&=&GMSMarker.markerImageWithColor(UIColor.redColor())
destinationMarker.title&=&self.mapTasks.destinationAddress注意,我们对标记设置了不同的颜色。更进一步的,这儿有一些新东西。在drawRoute()方法中,我们将在地图上绘制路线的线段。值得注意的是,利用GMSPath和GMSPolyline类,以及我们才能够MapTasks类中获取的路线点,创建路线是一件十分容易的事情。func&drawRoute()&{
&&&&let&route&=&mapTasks.overviewPolyline["points"]&as&String
&&&&let&path:&GMSPath&=&GMSPath(fromEncodedPath:&route)
&&&&routePolyline&=&GMSPolyline(path:&path)
&&&&routePolyline.map&=&viewMap
}在上面的代码片段的结尾,我们有必要将地图设置给routePolyline属性。此displayRouteInfo()方法十分简单。我们仅在屏幕底部的标签上显示了计算出的距离和位置:func&displayRouteInfo()&{
&&&&&lblInfo.text&=&mapTasks.totalDistance&+&"\n"&+&mapTasks.totalDuration
}这就是全部了。我们已经经历许多新的东西,但是一旦你了解了它们的含义,管理这一切就十分容易了。现在你可以再次测试此应用。指定起始和目的地址,让路线被创建出来。我必须重申的是,此概况折线在某些情况下并不是你所期待的那样准确,因此如果这对你来说还不够好的话,考虑一下实现之前我描述过的方案,遍历所有路程的所有步程,收集所有步程中的点,并将它们一个个地在地图上画出来。向路线中添加路径点航路点是路线的起始和目的点之间的位置,并且实际上是路线的一部分。目前,我们设法创建的路线,是开始和结束点之间没有任何东西的。现在我们将要添加航路点特性。因此,我们会使应用变得更加有趣,而这次不再使用警告控制器来输入路线的航路点的地址,我们仅需触碰地图,路线就会被重新计算。只要上述情况发生了,一个新的标记会被放置在地图上,指向触碰的位置。记住上述内容,我们在这需要做的第一件事情是在类中声明和初始化两个新的数组:var&markersArray:&Array=&[]
var&waypointsArray:&Array=&[]第一个数组持有指向航路点的所有标记。第二个数组,我们将航路点作为字符串对象的形式持有。现在往下继续,为了处理地图上的触碰并得到确切的触碰位置,我们需要使用GMSMapViewDelegate协议(存在于Google Maps SDK中)的一个代理方法。在我们完全实现它之前,我们需要继承此协议:class&ViewController:&UIViewController,&CLLocationManagerDelegate,&GMSMapViewDelegate&{
}我们也必须将ViewController类作为地图视图的代理。在viewDidLoad中添加如下简单的一行:override&func&viewDidLoad()&{
&&&viewMap.delegate&=&self
}让我们看看上述的代理方法,然后我们将对它进行讨论:func&mapView(mapView:&GMSMapView!,&didTapAtCoordinate&coordinate:&CLLocationCoordinate2D)&{
&&&&if&let&polyline&=&routePolyline&{
&&&&&&&&let&positionString&=&String(format:&"%f",&coordinate.latitude)&+&","&+&String(format:&"%f",&coordinate.longitude)
&&&&&&&&waypointsArray.append(positionString)
&&&&&&&&recreateRoute()
}在这里第一重要的细节是,确保有这么一条路线我们能够向它添加航路点。这很容易检查,因为我们只需确认路线的折线不是nil。在此情况下,我们很容易地使用坐标参数来组成一个字符串,包含了触碰位置的纬度和经度。请确保字符串里没有空格符,否则向Directions API的请求会失败。一旦此描述位置的字符串准备好了,我们就将它添加到waypointsArray中,并调用一个全新的自定义方法,recreateRoute()(显而易见,此方法会重新创建路线)。在我们重新绘制包含了新的航路点的路线之前,我们必须清除已有的路线。因此,我们首先创建了一个名为clearRoute()的新的函数,而没有实现recreateRoute()方法。让我们来看看:func&clearRoute()&{
&&&&originMarker.map&=&nil
&&&&destinationMarker.map&=&nil
&&&&routePolyline.map&=&nil
&&&&originMarker&=&nil
&&&&destinationMarker&=&nil
&&&&routePolyline&=&nil
&&&&if&markersArray.count&>&0&{
&&&&&&&&for&marker&in&markersArray&{
&&&&&&&&&&&&marker.map&=&nil
&&&&&&&&markersArray.removeAll(keepCapacity:&false)
}正如你所见到的,绘制过的路线中所有的对象都变为了nil并从地图中移除了。注意,我们甚至将markersArray中存在的标记的map属性都设置成了nil(我们还没有编写添加标记到此数组的代码,但是我们还是能做以上的事情)。现在,我们能实现recreateRoute()了:func&recreateRoute()&{
&&&&if&let&polyline&=&routePolyline&{
&&&&&&&&clearRoute()
&&&&&&&&mapTasks.getDirections(mapTasks.originAddress,&destination:&mapTasks.destinationAddress,&waypoints:&waypointsArray,&travelMode:&nil,&completionHandler:&{&(status,&success)&->&Void&in
&&&&&&&&&&&&if&success&{
&&&&&&&&&&&&&&&&self.configureMapAndMarkersForRoute()
&&&&&&&&&&&&&&&&self.drawRoute()
&&&&&&&&&&&&&&&&self.displayRouteInfo()
&&&&&&&&&&&&}
&&&&&&&&&&&&else&{
&&&&&&&&&&&&&&&&println(status)
&&&&&&&&&&&&}
&&&&&&&&})
}首先,我们要确保有一条路线,使我们能够清除它。然后我们向Directions API发出请求,并且注意到这次我们也指定了waypointsArray参数。如果一切正常,我们会调用之前章节里相同的方法,从而使路线被绘制出来。现在大部分的工作都做好了,但我们还是有两项任务需要完成。第一件事情是访问configureMapAndMarkersForRoute()方法,并且使它也具有添加航路点标记的能力。让我们来看看所需的添加能力:func&configureMapAndMarkersForRoute()&{
&&&&if&waypointsArray.count&>&0&{
&&&&&&&&for&waypoint&in&waypointsArray&{
&&&&&&&&&&&&let&lat:&Double&=&(ponentsSeparatedByString(",")[0]&as&NSString).doubleValue
&&&&&&&&&&&&let&lng:&Double&=&(ponentsSeparatedByString(",")[1]&as&NSString).doubleValue
&&&&&&&&&&&&let&marker&=&GMSMarker(position:&CLLocationCoordinate2DMake(lat,&lng))
&&&&&&&&&&&&marker.map&=&viewMap
&&&&&&&&&&&&marker.icon&=&GMSMarker.markerImageWithColor(UIColor.purpleColor())
&&&&&&&&&&&&markersArray.append(marker)
}请注意我们是怎样将每个航路点字符串分解成多个字段的,然后我们将经度和纬度转换成双精度字节(double)值,从而我们可以在初始化新的标记时使用它们。一旦匹配每个航路点的标记配置好了,我们就将它加入到markersArray数组里。第二项仍需要完成任务是,更新MapTasks.swift文件中的getDirections(…) 方法。在方法的开始处,强制性的更新directionsURLString字符串,从而在将要发出的请求中包含我们需要的航路点。注意,除了传递了航路点之外,我们也在查询中指定了另一个参数,请求当使用航路点时对路线进行优化:func&getDirections(origin:&String!,&destination:&String!,&waypoints:&Array!,&travelMode:&AnyObject!,&completionHandler:&((status:&String,&success:&Bool)&->&Void))&{
&&&&if&let&originLocation&=&origin&{
&&&&&&&&if&let&destinationLocation&=&destination&{
&&&&&&&&&&&&var&directionsURLString&=&baseURLDirections&+&"origin="&+&originLocation&+&"&destination="&+&destinationLocation
&&&&&&&&&&&&if&let&routeWaypoints&=&waypoints&{
&&&&&&&&&&&&&&&&directionsURLString&+=&"&waypoints=optimize:true"
&&&&&&&&&&&&&&&&for&waypoint&in&routeWaypoints&{
&&&&&&&&&&&&&&&&&&&&directionsURLString&+=&"|"&+&waypoint
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&}
&&&&&&&&&&&&...&&&&&&&&&&&
&&&&&&&&...
}现在你可以再试一试此应用。在你创建一条线路之后,触碰它周围任意一点,从而使得触碰位置作为航路点添加到路线中去。行进模式创建一条路线时,Directions API默认使用的是驾车模式。如我已经说过的,如下是Google Maps SDK在iOS中支持的行进模式:驾车模式步行模式自行车模式改变行进模式是一件有趣的事情,并且这也将是此示例应用十分好的一个特性,因此让我们在此部分实现它。我们要做的第一件事是创建一个枚举(enum),此枚举包含所有支持的行进模式。在ViewController.swift文件中,在文件顶部的类定义之前,增加如下代码行:enum&TravelModes:&Int&{
&&&&case&driving
&&&&case&walking
&&&&case&bicycling
}现在让我们在类中声明默认的行进模式:var&travelMode&=&TravelModes.driving为了改变行进模式,我们将用到changeTravelMode(_:) IBAction方法。在此方法中,我们新建了一个action sheet,提供给用户所有可能的选项。并且根据用户做出的选择,我们相应的设置行进模式。让我们看看代码:@IBAction&func&changeTravelMode(sender:&AnyObject)&{
&&&&let&actionSheet&=&UIAlertController(title:&"Travel&Mode",&message:&"Select&travel&mode:",&preferredStyle:&UIAlertControllerStyle.ActionSheet)
&&&&let&drivingModeAction&=&UIAlertAction(title:&"Driving",&style:&UIAlertActionStyle.Default)&{&(alertAction)&->&Void&in
&&&&&&&&self.travelMode&=&TravelModes.driving
&&&&&&&&self.recreateRoute()
&&&&let&walkingModeAction&=&UIAlertAction(title:&"Walking",&style:&UIAlertActionStyle.Default)&{&(alertAction)&->&Void&in
&&&&&&&&self.travelMode&=&TravelModes.walking
&&&&&&&&self.recreateRoute()
&&&&let&bicyclingModeAction&=&UIAlertAction(title:&"Bicycling",&style:&UIAlertActionStyle.Default)&{&(alertAction)&->&Void&in
&&&&&&&&self.travelMode&=&TravelModes.bicycling
&&&&&&&&self.recreateRoute()
&&&&let&closeAction&=&UIAlertAction(title:&"Close",&style:&UIAlertActionStyle.Cancel)&{&(alertAction)&->&Void&in
&&&&actionSheet.addAction(drivingModeAction)
&&&&actionSheet.addAction(walkingModeAction)
&&&&actionSheet.addAction(bicyclingModeAction)
&&&&actionSheet.addAction(closeAction)
&&&&presentViewController(actionSheet,&animated:&true,&completion:&nil)
}如你所注意到的,在每个action中我们设置了相关的行进模式,并且我们调用了之前部分中实现的recreateRoute()方法。当然,我们在关闭动作(close action)中不会这样做,而只是取消掉action sheet。以上所述都还不会产生效果,因为recreateRoute()方法需要更新。在当前实现里,当调用MapTasks类的getDirections(…)方法时,我们不会将行进模式作为参数来提供给此方法;相反的,我们将行进模式设置为nil。因此,让我们做些修正:func&recreateRoute()&{
&&&&if&let&polyline&=&routePolyline&{
&&&&&&&&...
&&&&&&&&mapTasks.getDirections(mapTasks.originAddress,&destination:&mapTasks.destinationAddress,&waypoints:&waypointsArray,&travelMode:&travelMode,&completionHandler:&{&(status,&success)&->&Void&in
&&&&&&&&&&&&...
&&&&&&&&})
}这里唯一改变了的是travelMode参数。现在,我们也需要更新getDirections(…)方法,从而使它考虑我们设置的行进模式。因此,来到MapTasks.swift文件中,作如下增添:func&getDirections(origin:&String!,&destination:&String!,&waypoints:&Array!,&travelMode:&TravelModes!,&completionHandler:&((status:&String,&success:&Bool)&->&Void))&{
&&&&if&let&originLocation&=&origin&{
&&&&&&&&if&let&destinationLocation&=&destination&{
&&&&&&&&&&&&var&directionsURLString&=&baseURLDirections&+&"origin="&+&originLocation&+&"&destination="&+&destinationLocation
&&&&&&&&&&&&...
&&&&&&&&&&&&if&let&travel&=&travelMode&{
&&&&&&&&&&&&&&&&var&travelModeString&=&""
&&&&&&&&&&&&&&&&switch&travelMode.rawValue&{
&&&&&&&&&&&&&&&&case&TravelModes.walking.rawValue:
&&&&&&&&&&&&&&&&&&&&travelModeString&=&"walking"
&&&&&&&&&&&&&&&&case&TravelModes.bicycling.rawValue:
&&&&&&&&&&&&&&&&&&&&travelModeString&=&"bicycling"
&&&&&&&&&&&&&&&&default:
&&&&&&&&&&&&&&&&&&&&travelModeString&=&"driving"
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&directionsURLString&+=&"&mode="&+&travelModeString
&&&&&&&&&&&&}
&&&&&&&&&&&&...
&&&&&&&&...
}注意:比增加if let travel = travelMode{ … } 语句更进一步的,我们也需要改变travelMode的参数类型,从AnyObject! 变为TravelModes!(我们在之前创建的枚举类型)。现在我们做得够好了,可以再次测试此应用了。去改变一下行进模式,然后注意到路线信息,特别是旅程的时间,是如何改变的。最后的调试在此我们的示例应用几乎完成了。我说几乎是因为还有两个细节需要增加到我们的代码中,从而使应用尽可能好的工作。我们必须做的第一件事,是在将要创建新线路之前,清除存在的路线。因此,在createRoute(_:)方法的createRouteAction块中,我们需要检查是否已经存在一条路线,然后将它清除:@IBAction&func&createRoute(sender:&AnyObject)&{
&&&&...&&&&
&&&&let&createRouteAction&=&UIAlertAction(title:&"Create&Route",&style:&UIAlertActionStyle.Default)&{&(alertAction)&->&Void&in
&&&&&&&&if&let&polyline&=&self.routePolyline&{
&&&&&&&&&&&&self.clearRoute()
&&&&&&&&&&&&self.waypointsArray.removeAll(keepCapacity:&false)
&&&&&&&&...
}注意:除了清除掉路线之外,我们也从waypointsArray中溢出了所有对象。因为我们要创建一条新路线,没有理由保存任何已有的航路点。第二件事情是再次指定行进模式作为IBAction方法的一个参数,因为目前我们仅将相关参数的值设置为了nil:@IBAction&func&createRoute(sender:&AnyObject)&{
&&&&...&&&&
&&&&let&createRouteAction&=&UIAlertAction(title:&"Create&Route",&style:&UIAlertActionStyle.Default)&{&(alertAction)&->&Void&in
&&&&&&&&...
&&&&&&&&self.mapTasks.getDirections(origin,&destination:&destination,&waypoints:&nil,&travelMode:&self.travelMode,&completionHandler:&{&(status,&success)&->&Void&in
&&&&&&&&&&&&...
}在完成上述两项任务之后,可以说我们的示例应用最终完成了。总结在此入门教程的各部分里,我们设法将开发者使用地图时的最重要的任务都做了一遍,并且在此使用的是iOS版本的Google Maps SDK。如果你要使用Google地图,那么你已阅读过的上述所有章节能将你带上正确的轨道,而且现在你肯定有了一个工作的起点。无需置疑的是,有许多关于Google Maps SDK的细节在此(仅一篇)入门教程中无法被提及。然而,你现在已经知道了此SDK基本的和最重要的方面,你可以投入精力到中,查询你所需的任何额外的信息。当然,使用地图是十分有趣的课题,并且无论可能出现什么困难,到最后它依然是值得使用的东西。无论如何,如果你当前正在使用或者如果你计划使用Google Maps SDK,我希望你对此帖子提供的信息感到有用。将它当作一篇指南,没什么可以阻挡你深入其中并充分利用所有给出的API。作为一项参考,你可以在此处程。像往常一样,请给我评论并分享你对此入门教程的想法。
微信扫一扫
订阅每日移动开发及APP推广热点资讯公众号:CocoaChina
您还没有登录!请或
点击量16272点击量10875点击量8664点击量8017点击量7488点击量7063点击量6588点击量6497点击量5716
&2016 Chukong Technologies,Inc.
京公网安备89

我要回帖

更多关于 传奇怎么添加地图 的文章

 

随机推荐