MapKit框架详细解析(二) —— 基本使用简单示例(一)

版本记录

版本号 时间
V1.0 2018.10.14 星期日

前言

MapKit框架直接从您的应用界面显示地图或卫星图像,调出兴趣点,并确定地图坐标的地标信息。接下来几篇我们就一起看一下这个框架。感兴趣的看下面几篇文章。
1. MapKit框架详细解析(一) —— 基本概览(一)

开始

首先看一下本文的写作环境

Swift 4, iOS 11, Xcode 9

MapKit是iOS设备上非常有用的API,可以轻松显示地图,绘制位置,甚至在上面绘制路线和其他形状。

此更新使用来自檀香山的公共艺术品数据。在本教程中,您将制作一个放大檀香山位置的应用程序,并在地图上绘制其中一件作品。 您将实施pin的标注详细信息按钮以启动Maps应用程序,其中包含对艺术品的驾驶/步行路线。 然后,您的应用程序将从Honolulu数据门户解析JSON文件,以提取公共艺术品对象,并在地图上绘制它们。

在此过程中,您将学习如何将MapKit地图添加到您的应用程序,缩放到特定位置,解析使用Socrata Framework的政府数据,创建自定义地图annotations等等!

打开入门项目,其中包含JSON文件和一些图像资源,但还没有地图!

打开Main.storyboard。 在File Inspector中,选中Use Safe Area Layout Guides框。 这将阻止您设置相对于已弃用的布局指南的约束,并停止deprecated警告。

Document Outline中,选择Safe Area,以查看其上边缘略低于视图的上边缘。 从Object library中,将MapKit View拖动到场景的上角,将其顶部边缘与视图顶部边缘下方的蓝色虚线对齐,然后拖动其右下角以与视图的右下角相交。 使用Add New Constraints自动布局菜单(TIE战斗机图标)固定地图视图:取消选中Constrain to margins,然后将所有邻值设置为0,并单击Add 4 constraints

注意:通常情况下,您不必手动将地图视图拉伸到场景中 - 只需使用Add New Constraints菜单来固定其边缘 - 但这在Xcode 9 beta中尚未使用。现在Xcode 10都出来了,所以这个问题已经不存在了。

接下来,将此行添加到ViewController.swift,就在import UIKit语句的下方:

import MapKit

Build并运行您的项目,您将拥有一个完全可缩放和可平移的地图,使用Apple Maps显示您当前位置的大陆!

到目前为止这么好,嗯? 但是你不想开始查看整个世界的地图 - 你想放大到一个特定的区域!

要控制地图视图,必须在ViewController.swift中为其创建outlet

在故事板中,打开assistant editor:它应该显示ViewController.swift

要创建outlet,请单击Main.storyboard中的Map View,然后按住control - 拖动到ViewController类定义内的空间:Xcode应提示Insert Outlet or Outlet Collection。 释放拖动,然后在弹出窗口中为outlet命名为mapView

Xcode将一个mapView属性添加到ViewController类:您将使用它来控制地图视图显示的内容。


Setting Visible Area - 设置可见区域

切换回standard editor,在ViewController.swift中找到viewDidLoad(),并将以下内容添加到方法的末尾:

// set initial location in Honolulu
let initialLocation = CLLocation(latitude: 21.282778, longitude: -157.829444)

您将使用它将地图视图的起始坐标设置为檀香山的一个点。

在告诉地图要显示的内容时,给出纬度和经度足以使地图居中,但您还必须指定要显示的矩形区域,以获得正确的缩放级别。

将以下常量和辅助方法添加到类中:

let regionRadius: CLLocationDistance = 1000
func centerMapOnLocation(location: CLLocation) {
  let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate, 
    regionRadius, regionRadius)
  mapView.setRegion(coordinateRegion, animated: true)
}

location参数是中心点。 该区域将根据区域的距离而具有南北和东西跨度。 您将其设置为1000米:稍微超过半英里,这适用于在JSON文件中绘制艺术品数据。

setRegion(_:animated :)告诉mapView显示该区域。 地图视图使用简洁的缩放动画自动将当前视图转换到所需区域,无需额外代码!

回到viewDidLoad(),将以下行添加到方法的末尾:

centerMapOnLocation(location: initialLocation)

您正在调用辅助方法以在启动时放大initialLocation

Build并运行应用程序,您将发现自己位于Waikiki(地名)的中心。


Obtaining Public Artworks Data - 获取公共艺术品数据

下一步是在当前位置周围绘制有趣的数据。但是在哪里可以得到这样的东西?

那么,这取决于你当前的位置。与许多城市一​​样,檀香山有一个 Open Data Portal,可以改善公众对政府数据的访问。与许多城市一​​样,檀香山的数据门户是Socrata提供支持的,这是一个开放的数据框架,提供了一组丰富的developer tools,用于访问基于Socrata的数据。完成本教程后,可以查看附近的城市是否有可以使用的备用数据集?

在本教程中,您将使用Honolulu Public Art。为了简单起见,我已经从门户网站下载了这些数据,并将其包含在项目中。

要了解此数据集中的项目,请在Xcode编辑器中打开PublicArt.json,然后向下滚动到第1180行(或使用⌘+ L表示跳转到行),以“data”开头,后跟数组数组 - 每个艺术品一个数组。对于本教程,您将仅使用每个数组中的一些属性:艺术作品的location name, discipline, title, latitude and longitude。例如,对于第一个数据项:

  • location name - 地点名称Lester McCoy Pavilion
  • disciplineMural
  • title - 标题The Makahiki Festival – The Makai Mural
  • latitude - 纬度21.290824
  • longitude - 经度-157.85131

在本教程的后面,您将解析此数据集以创建一艺术作品数组,但首先,要直接跳转到MapKit,您只需在地图上绘制其中一件作品。


Showing an Artwork on the Map - 在地图上显示艺术品

PublicArt.json中,跳转或滚动到第1233行的项目55:它是威基基盖特威公园的大卫卡拉卡瓦国王的青铜雕像。

Photo of King David Kalakaua statue, by Wally Gobetz

该项目的属性是:

  • location name - 地点名称:威基基盖特威公园
  • discipline:雕塑
  • title - 标题:大卫卡拉卡瓦国王
  • latitude - 纬度21.283921
  • longitude - 经度-157.831661

要在地图视图上显示此内容,您必须创建map annotationmap annotation是绑定到特定位置的小块信息,并且通常在Apple的Maps应用中表示为pins

要创建自己的annotations,可以创建符合MKAnnotation协议的类,将annotations添加到地图,并通知地图应如何显示annotations

1. The Artwork Class - Artwork类

首先,在新的Swift文件中创建一个Artwork类:File \ New \ File,选择iOS \ Source \ Swift File,然后单击Next。 将Save As设置为Artwork.swift,然后单击Create

在编辑器中打开Artwork.swift并在import Foundation下面添加以下内容:

import MapKit

class Artwork: NSObject, MKAnnotation {
  let title: String?
  let locationName: String
  let discipline: String
  let coordinate: CLLocationCoordinate2D
  
  init(title: String, locationName: String, discipline: String, coordinate: CLLocationCoordinate2D) {
    self.title = title
    self.locationName = locationName
    self.discipline = discipline
    self.coordinate = coordinate
    
    super.init()
  }
  
  var subtitle: String? {
    return locationName
  }
}

要采用MKAnnotation协议,Artwork必须是NSObject的子类,因为MKAnnotation是一个NSObjectProtocol

MKAnnotation协议需要coordinate属性。 如果您希望annotation view在用户点击pin时显示标题和副标题,则您的类还需要名为titlesubtitle的属性。

对于Artwork类来说,存储名为titlecoordinate的属性是完全明智的,但是没有一个PublicArt.json属性自然地映射到subtitle的概念。 要符合MKAnnotation协议,您可以使subtitle成为返回locationName的计算属性。

好的,所以title, locationNamecoordinate属性将用于MKAnnotation对象,但是discipline属性用来做什么?你会在本教程的后面找到!

2. Adding an Annotation - 添加注释

接下来,您将为地图视图添加一个Artwork对象,用于您要绘制的每件艺术品。 目前,您只添加了一个艺术作品,因此切换到ViewController.swift并将以下行添加到viewDidLoad()的末尾:

// show artwork on map
let artwork = Artwork(title: "King David Kalakaua",
  locationName: "Waikiki Gateway Park", 
  discipline: "Sculpture",
  coordinate: CLLocationCoordinate2D(latitude: 21.283921, longitude: -157.831661))
mapView.addAnnotation(artwork)

在这里,您将创建一个新的Artwork对象,并将其作为annotation添加到地图视图中。 MKMapView类还有一个addAnnotations :(复数)方法,当你有一个annotation数组要添加到地图视图时,你将在本教程的后面使用它。

Build并运行你的项目,现在你应该看到King David Kalakaua的雕像在Waikiki的入口处!

默认annotation标记视图显示位置,标记下方是标题。 选择标记:它会变大,现在也会显示副标题:

嗯,没关系,但是当用户点击标记时,你习惯于显示标注的pins - 一个小气泡。 为此,您必须配置annotation视图,这是下一步。

3. Configuring the Annotation View - 配置注释视图

配置annotation视图的一种方法是实现地图视图的mapView(_:viewFor :)代理方法。 您在此委托方法中的工作是返回MKAnnotationView的实例,以作为annotation的可视指示器呈现。

在这种情况下,ViewController将成为地图视图的代理。 为了避免混乱并提高可读性,您将创建ViewController类的扩展。

ViewController.swift的底部添加以下内容:

extension ViewController: MKMapViewDelegate {
  // 1
  func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
    // 2
    guard let annotation = annotation as? Artwork else { return nil }
    // 3
    let identifier = "marker"
    var view: MKMarkerAnnotationView
    // 4
    if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
      as? MKMarkerAnnotationView { 
      dequeuedView.annotation = annotation
      view = dequeuedView
    } else {
      // 5
      view = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
      view.canShowCallout = true
      view.calloutOffset = CGPoint(x: -5, y: 5)
      view.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
    }
    return view
  }
}

这是你在做的事情:

  • 1) mapView(_:viewFor :)会为您添加到地图的每个annotation调用(就像使用表视图时的tableView(_:cellForRowAt :)一样),以返回每个annotation的视图。
  • 2) 您的应用可能会使用其他annotation,例如用户位置,因此请检查此annotation是否为Artwork对象。 如果不是,则返回nil以使地图视图使用其默认annotation视图。
  • 3) 要显示标记,可以将每个视图创建为MKMarkerAnnotationView。 在本教程的后面,您将创建MKAnnotationView对象,以显示图像而不是标记。
  • 4) 与tableView(_:cellForRowAt :)类似,地图视图也会重用不再可见的annotation视图。 因此,在创建新的annotation视图之前,请检查是否可以使用可重用的annotation视图。
  • 5) 如果annotation视图无法重用出列,则在此处创建新的MKMarkerAnnotationView对象。 它使用Artwork类的标题和副标题属性来确定要在标注中显示的内容。

注意:有一点需要注意的事情,由Kalgar建议,当你将可重复使用的annotation出列时,你给它一个标识符。 如果您有多种annotation样式,请确保每个annotation都有唯一的标识符,否则您可能会错误地将其他类型的标识符出列,并在您的应用中出现意外行为。 同样,它与tableView(_:cellForRowAt :)中的单元标识符背后的想法相同。

剩下的就是将ViewController设置为地图视图的代理。 您可以在Main.storyboard中执行此操作,但我更喜欢在代码中执行此操作,它更加明显。 在ViewController.swift中,在创建artwork的语句之前,将此行添加到viewDidLoad()

mapView.delegate = self

就是这样! Build并运行项目,然后点击标记以弹出标注气泡:

mapView(_:viewFor :)将标注配置为在右侧包含详细信息披露信息按钮,但点击该按钮尚未执行任何操作。 您可以实现它以显示包含更多信息的alert弹窗,或者打开详细视图控制器。

这是一个很好的第三个选项:当用户点击信息按钮时,您的应用程序将启动Maps应用程序,完成驾驶/步行/公交路线,从模拟用户位置到艺术品!


Launching the Maps App - 启动Maps应用程序

要提供这种出色的用户体验,请打开Artwork.swift并在其他两个下面添加此import语句:

import Contacts

这将添加Contacts框架,其中包含字典键常量,例如CNPostalAddressStreetKey,用于需要设置位置的地址,城市或州字段。

接下来,将以下辅助方法添加到类中:

// Annotation right callout accessory opens this mapItem in Maps app
func mapItem() -> MKMapItem {
  let addressDict = [CNPostalAddressStreetKey: subtitle!]
  let placemark = MKPlacemark(coordinate: coordinate, addressDictionary: addressDict)
  let mapItem = MKMapItem(placemark: placemark)
  mapItem.name = title
  return mapItem
}

在这里,您可以从MKPlacemark创建MKMapItem。 地图应用程序能够读取此MKMapItem,并显示正确的内容。

接下来,您必须告诉MapKit当用户点击callout按钮时该怎么做。 打开ViewController.swift,并将此方法添加到MKMapViewDelegate扩展:

func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView,
    calloutAccessoryControlTapped control: UIControl) {
  let location = view.annotation as! Artwork
  let launchOptions = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving]
  location.mapItem().openInMaps(launchOptions: launchOptions)
}

当用户点击地图annotation标记时,标注会显示一个信息按钮。如果用户点击此信息按钮,则调用mapView(_:annotationView:calloutAccessoryControlTapped :)方法。

在此方法中,您将获取此点击要引用的Artwork对象,然后通过创建关联的MKMapItem并在map item上调用openInMaps(launchOptions :)来启动Maps应用。

请注意,您正在将字典传递给此方法。这允许您指定几个不同的选项;这里DirectionModeKey设置为Driving。这会导致Maps应用显示从用户当前位置到此pin的行车路线。

注意:浏览MKMapItem documentation以查看其他启动选项字典键,以及openMaps(with:launchOptions :)方法,该方法允许您传递MKMapItem对象数组。

在你Build和运行之前,你应该搬到檀香山 - 实际上,只需将你的模拟位置设置为檀香山。在Xcode中,转到Product \ Scheme \ Edit Scheme ...,从左侧菜单中选择Run,然后选择Options选项卡。检查Core Location: Allow Location Simulation,并选择Honolulu, HI, USA作为Default Location。然后单击Close按钮:

Build并运行应用程序,您将看到地图放大Waikiki,就像之前一样。 点击标记,然后点击标注中的info按钮,并观看它启动Maps应用以显示雕像的位置,并显示其行车路线:

注意:首次打开Maps时,系统会提示您允许Maps访问您的位置(点按允许),并显示安全警告。

后记

本篇主要讲述了基本使用简单示例,感兴趣的给个赞或者关注~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容