MapKit框架詳細(xì)解析(九) —— 地圖特定區(qū)域放大和創(chuàng)建自定義地圖annotations(一)

版本記錄

版本號(hào) 時(shí)間
V1.0 2020.04.26 星期日

前言

MapKit框架直接從您的應(yīng)用界面顯示地圖或衛(wèi)星圖像著拭,調(diào)出興趣點(diǎn)鸵闪,并確定地圖坐標(biāo)的地標(biāo)信息燥狰。接下來幾篇我們就一起看一下這個(gè)框架妆绞。感興趣的看下面幾篇文章板惑。
1. MapKit框架詳細(xì)解析(一) —— 基本概覽(一)
2. MapKit框架詳細(xì)解析(二) —— 基本使用簡單示例(一)
3. MapKit框架詳細(xì)解析(三) —— 基本使用簡單示例(二)
4. MapKit框架詳細(xì)解析(四) —— 一個(gè)疊加視圖相關(guān)的簡單示例(一)
5. MapKit框架詳細(xì)解析(五) —— 一個(gè)疊加視圖相關(guān)的簡單示例(二)
6. MapKit框架詳細(xì)解析(六) —— 添加自定義圖塊(一)
7. MapKit框架詳細(xì)解析(七) —— 添加自定義圖塊(二)
8. MapKit框架詳細(xì)解析(八) —— 添加自定義圖塊(三)

開始

首先看下主要內(nèi)容:

了解如何使用功能強(qiáng)大的MapKit框架來構(gòu)建交互式地圖裆熙,顯示位置詳細(xì)信息并啟動(dòng)用于行車路線的地圖端礼。文章來自翻譯

下面看下寫作環(huán)境

Swift 5, iOS 13, Xcode 11

MapKitiOS設(shè)備上可用的功能強(qiáng)大的API入录,可輕松顯示地圖蛤奥,標(biāo)記位置,增強(qiáng)自定義數(shù)據(jù)甚至在頂部繪制路線或其他形狀僚稿。

在此MapKit教程中凡桥,您將制作HonoluluArt,此應(yīng)用程序可縮放到Honolulu中的位置并在地圖上標(biāo)記公共藝術(shù)品蚀同。 您將實(shí)現(xiàn)標(biāo)記的標(biāo)注詳細(xì)信息按鈕缅刽,以啟動(dòng)Maps應(yīng)用并打開藝術(shù)品的行車路線。 然后蠢络,您將從Honolulu city數(shù)據(jù)門戶網(wǎng)站解析GeoJSON文件拷恨,以提取公共藝術(shù)品特征并將其標(biāo)記在地圖上。

在此過程中谢肾,您將學(xué)習(xí)如何:

  • MapKit地圖添加到您的應(yīng)用腕侄。
  • 縮放到特定位置。
  • 解析GeoJSON數(shù)據(jù)以創(chuàng)建自定義地圖注釋。

打開開始項(xiàng)目中的Main.storyboard冕杠,然后從Object library中將Map Kit View拖到場景的中間微姊。 將Map View限制為父視圖(而不是“安全區(qū)域”),并在所有邊緣上將其設(shè)置為0分预,以便將其擴(kuò)展到帶缺口設(shè)備的“安全區(qū)域”中兢交。

構(gòu)建并運(yùn)行。 現(xiàn)在笼痹,您可以使用Apple Maps完全縮放和平移的地圖配喳,顯示您當(dāng)前位置的大陸!

到目前為止凳干,很好晴裹,是嗎?

您想放大特定的區(qū)域救赐。 為此涧团,您需要獲取編碼!

打開ViewController.swift并在import UIKit語句下面添加以下內(nèi)容:

import MapKit

接下來经磅,您需要在ViewController.swift中的MKMapView設(shè)置outlet泌绣。

viewDidLoad()之前添加以下outlet代碼:

@IBOutlet private var mapView: MKMapView!

然后,轉(zhuǎn)到Main.storyboard并將Map View鏈接到新的outlet


Setting the Visible Area

回到ViewController预厌。找到viewDidLoad()阿迈,并在方法末尾添加以下內(nèi)容:

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

您將使用此設(shè)置將地圖視圖的起始坐標(biāo)設(shè)置為Honolulu的一個(gè)點(diǎn)。

在告訴地圖要顯示什么內(nèi)容時(shí)轧叽,提供緯度和經(jīng)度就足以使地圖居中仿滔。但是,您還必須指定要顯示的矩形區(qū)域犹芹,以獲得正確的縮放級(jí)別。

ViewController.swift的末尾添加以下私有擴(kuò)展:

private extension MKMapView {
  func centerToLocation(
    _ location: CLLocation, 
    regionRadius: CLLocationDistance = 1000
  ) {
    let coordinateRegion = MKCoordinateRegion(
      center: location.coordinate,
      latitudinalMeters: regionRadius,
      longitudinalMeters: regionRadius)
    setRegion(coordinateRegion, animated: true)
  }
}

location參數(shù)是中心點(diǎn)鞠绰。根據(jù)regionRadius的距離腰埂,該區(qū)域?qū)⒕哂心媳焙蜄|西跨度,默認(rèn)為1000米蜈膨,略多于半英里屿笼,這對于在GeoJSON文件中繪制公共藝術(shù)品數(shù)據(jù)非常有用。

setRegion(_:animated:)告訴MKMapView顯示由MKCoordinateRegion表示的區(qū)域翁巍。地圖視圖會(huì)自動(dòng)將當(dāng)前視圖轉(zhuǎn)換為所需的區(qū)域驴一,帶有一個(gè)整潔的縮放動(dòng)畫,不需要額外的代碼!

viewDidLoad()中灶壶,在方法的末尾添加以下行:

mapView.centerToLocation(initialLocation)

這將調(diào)用helper方法以在啟動(dòng)時(shí)放大到initialLocation肝断。

構(gòu)建和運(yùn)行。你會(huì)發(fā)現(xiàn)自己在Waikiki. Aloha的中心。


Constraining the Camera

到目前為止胸懈,你可以去地圖上任何你想去的地方担扑,但我們只對Oahu興趣。你還可以在縮放地圖到目前為止趣钱,島嶼只有幾個(gè)像素涌献!MapKit能夠約束用戶在指定區(qū)域內(nèi)平移和縮放地圖。

注意:要放大模擬器首有,按住Option并拖動(dòng)地圖視圖燕垃。
同樣在viewDidLoad()中,在方法的末尾添加以下行:

    let oahuCenter = CLLocation(latitude: 21.4765, longitude: -157.9647)
    let region = MKCoordinateRegion(
      center: oahuCenter.coordinate,
      latitudinalMeters: 50000,
      longitudinalMeters: 60000)
    mapView.setCameraBoundary(
      MKMapView.CameraBoundary(coordinateRegion: region),
      animated: true)
    
    let zoomRange = MKMapView.CameraZoomRange(maxCenterCoordinateDistance: 200000)
    mapView.setCameraZoomRange(zoomRange, animated: true)

構(gòu)建和運(yùn)行【現(xiàn)在當(dāng)你縮小的時(shí)候卜壕,你不能讓Oahu像以前一樣小。你只能平移地圖視圖來查看島的北部低矮、南部印叁、東部和西部的邊緣。

MapKit使用一個(gè)內(nèi)部攝像機(jī)來確定地圖的視點(diǎn)在哪里军掂,視場的范圍有多大轮蜕,并使視點(diǎn)移動(dòng)。當(dāng)您指定一個(gè)具有中心和區(qū)域矩形的攝像機(jī)邊界時(shí)蝗锥,攝像機(jī)中心始終位于此區(qū)域內(nèi)。

如果用戶試圖移出該區(qū)域汇竭,則視圖不會(huì)進(jìn)一步滾動(dòng)穴张。讓他們專注于你想讓他們看到的地方细燎。6萬米寬皂甘、5萬米高的區(qū)域大小很好地限制了Oahu的地圖視野。

MapKit視圖攝像機(jī)在縮放時(shí)跟蹤它與視圖中心的距離偿枕。設(shè)置相機(jī)的縮放范圍最大中心距離約束限制了視野縮小的距離和Oahu島變得多么小。這在向地圖添加興趣點(diǎn)(points of interest)時(shí)更有幫助渐夸,這是下一步!


Obtaining Public Art Data

下一步是圍繞當(dāng)前位置繪制有趣的數(shù)據(jù)墓塌。

嗯,這取決于你現(xiàn)在的位置酷誓。和許多城市一樣盐数,Honolulu也有一個(gè)開放的數(shù)據(jù)門戶 Open Data Portal玫氢,以改善公眾對政府?dāng)?shù)據(jù)的訪問漾峡。

Honolulu數(shù)據(jù)門戶的一個(gè)方便特性是能夠以GeoJSON格式導(dǎo)出數(shù)據(jù)集——一種JSON格式,用于表示地理空間特性及其元數(shù)據(jù)牢屋。MapKit最近的一個(gè)特性使得在您的應(yīng)用程序中使用GeoJSON數(shù)據(jù)變得超級(jí)容易烙无,您很快就會(huì)看到這一點(diǎn)。

注意:在您完成本教程之后遍尺,查看附近的城市是否有另一個(gè)您可以使用的GeoJSON格式的數(shù)據(jù)集截酷。一旦你看到它是多么容易迂苛,你可能會(huì)發(fā)現(xiàn)它有趣的繪圖在同一地圖上的不同數(shù)據(jù)集三幻。

在本教程中念搬,您將使用檀香山公共藝術(shù)Honolulu Public Art數(shù)據(jù)集。為了簡單起見懊蒸,我已經(jīng)從門戶導(dǎo)出了這些數(shù)據(jù)骑丸,并將其包含在starter項(xiàng)目中通危,當(dāng)然菊碟,為了便于閱讀逆害,這些數(shù)據(jù)經(jīng)過了良好的格式化魄幕。這是我最起碼能做的纯陨。

要了解此數(shù)據(jù)集中的項(xiàng)翼抠,請打開Xcode編輯器中的PublicArt.geojson机久。在頂層你會(huì)看到:

{
  "type": "FeatureCollection",
  "features": [ ... ]
}

features的值是類型為FeatureGeoJSON對象數(shù)組膘盖。每個(gè)對象都包含一些標(biāo)準(zhǔn)鍵侠畔,以及一個(gè)properties字典软棺,其中包含由數(shù)據(jù)門戶創(chuàng)建的一些自定義字段喘落。geometry鍵是你找到特征的坐標(biāo)的地方瘦棋。

看看第一個(gè):

{
  "type":"Feature",
  "properties":{
    "location":"Lester McCoy Pavilion",
    "latitude":"21.290824",
    "description":"...",
    "thumb":null,
    "credit":"Funded by the Works Progress Administration",
    "objectid":"1930.01.01",
    "creator":"Robert Lee Eskridge",
    "longitude":"-157.85131",
    "imagefile":"http://....JPG",
    "date":"1935",
    "discipline":"Mural",
    "title":"The Makahiki Festival - The Makai Mural",
    "access":"Limited"
  },
  "geometry":{
    "type":"Point",
    "coordinates":[-157.85131,21.290824]
  }
}

1. GeoJSON Properties

不要太擔(dān)心GeoJSON數(shù)據(jù)格式凰狞。您很快就會(huì)看到赡若,導(dǎo)入它基本上是自動(dòng)的逾冬!基本部分是屬性字典和坐標(biāo)粉渠。

對于本教程霸株,您將只使用幾個(gè)屬性:藝術(shù)品的位置名稱去件、規(guī)程尤溜、標(biāo)題宫莱、緯度和經(jīng)度:

  • Location name: Lester McCoy Pavilion.
  • Discipline: Mural.
  • Title: The Makahiki Festival – The Makai Mural.
  • Latitude: 21.290824.
  • Longitude: -157.85131.

注意:如果你想了解更多關(guān)于格式的信息授霸,你可以探索整個(gè)世界碘耳。官方網(wǎng)站official website是有點(diǎn)稀疏辛辨,RFC是高度技術(shù)性的斗搞,但維基百科Wikipedia article的文章是可讀的僻焚。

它是一種廣泛支持的格式溅呢,適用于所有地理空間方面的內(nèi)容咐旧。一些有用的工具包括geojson.io是一個(gè)測試和編輯GeoJSON數(shù)據(jù)的有用站點(diǎn)铣墨,GitHub將自動(dòng)在地圖上的存儲(chǔ)庫中呈現(xiàn)任何.geojson文件伊约。

在本教程的后面屡律,您將使用整個(gè)數(shù)據(jù)集搏讶。但首先媒惕,為了直接進(jìn)入MapKit妒蔚,您將在地圖上繪制一個(gè)藝術(shù)品肴盏。


Showing Artwork on the Map

PublicArt.geojson叁鉴,按下Command-L幌墓,跳到第1354行常侣。這是威基基公園(Waikiki Gateway Park)里的大衛(wèi)王卡拉卡瓦King David Kalakaua的青銅雕像胳施。

Photo of King David Kalakaua statue, by Wally Gobetz

這個(gè)項(xiàng)目的屬性是:

  • Location name: Waikiki Gateway Park
  • Discipline: Sculpture
  • Title: King David Kalakaua
  • Latitude: 21.283921
  • Longitude: -157.831661

要在地圖視圖中顯示這一點(diǎn)焦辅,您必須創(chuàng)建一個(gè)map annotation筷登。map annotation是綁定到特定位置的小塊信息前方。蘋果的Maps應(yīng)用程序通常將它們表示為小圖釘惠险。

要?jiǎng)?chuàng)建注釋annotations班巩,您需要?jiǎng)?chuàng)建一個(gè)符合MKAnnotation的類,將該注釋添加到地圖中遥缕,并告訴地圖該如何顯示該注釋单匣。

1. The Artwork Class

首先户秤,在“項(xiàng)目導(dǎo)航器”中右鍵單擊HonoluluArt文件夾鸡号,選擇New File…鲸伴,選擇Swift File,并將新文件命名為Artwork.swift仲吏。

在編輯器中打開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许帐,因?yàn)?code>MKAnnotation是一個(gè)NSObjectProtocol

MKAnnotation需要coordinate屬性羡鸥。如果您希望注釋視圖在用戶點(diǎn)擊標(biāo)記時(shí)顯示標(biāo)題和副標(biāo)題惧浴,那么您的類還需要名為titlesubtitle的屬性衷旅。

Artwork類完全可以存儲(chǔ)名為titlecoordinate的屬性柿顶,而不存儲(chǔ)PublicArt.geojson屬性自然地映射到subtitle嘁锯。要符合MKAnnotation,可以將subtitle作為返回locationName的計(jì)算屬性仁锯。

MKAnnotation協(xié)議屬性titlesubtitle被定義為可選的字符串业崖,但是您可能想知道為什么要使用String?作為locationNamediscipline屬性的類型腻要。由于這些屬性是從外部數(shù)據(jù)源PublicArt.geojson設(shè)置文件雄家,你不能保證它們總是存在乱投。最好是安全一點(diǎn)戚炫。

你會(huì)使用MKAnnotation對象的title双肤,locationNamecoordinate屬性,但它的discipline屬性是什么呢蔑赘?您將在本教程的后面找到答案缩赛!

2. Adding an Annotation

接下來酥馍,您將為您想要繪制的每個(gè)藝術(shù)品向地圖視圖添加一個(gè)Artwork對象∥锱纾現(xiàn)在,您只添加了一個(gè)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)

在這里隧魄,您創(chuàng)建一個(gè)新的Artwork對象,并將其作為注釋添加到地圖視圖中狮含。MKMapView還提供了addAnnotations(_:)几迄,在本教程后面的內(nèi)容中木羹,當(dāng)您有一個(gè)要添加到map視圖中的annotation數(shù)組時(shí)坑填,您將會(huì)用到它脐瑰。

構(gòu)建和運(yùn)行≈严В現(xiàn)在你可以看到大衛(wèi)王卡拉卡瓦(King David Kalakaua)的雕像就在Waikiki的門口!

默認(rèn)的annotation標(biāo)記視圖顯示標(biāo)題位于標(biāo)記下方的位置抒寂。選擇標(biāo)記郊愧。它變大属铁,現(xiàn)在也顯示子標(biāo)題:

好吧,這沒什么例嘱,但你已經(jīng)看到,在其他應(yīng)用程序中點(diǎn)擊一個(gè)標(biāo)記會(huì)顯示一個(gè)標(biāo)注(callout):一個(gè)小小的方形語音氣泡勒虾。為此详羡,您必須配置annotation視圖实柠。

3. Configuring the Annotation View

配置annotation視圖的一種方法是實(shí)現(xiàn)map視圖的mapView(_:viewFor:)代理方法窒盐。在這個(gè)代理方法中,您的工作是返回MKAnnotationView的一個(gè)實(shí)例葡粒,作為annotation的一個(gè)可視指示器。

在本例中夫壁,ViewController是地圖視圖的代理盒让。為了避免混亂和提高可讀性邑茄,您將創(chuàng)建一個(gè)ViewController擴(kuò)展。

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 = "artwork"
    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:)在處理table view時(shí),對于添加到地圖中的每一個(gè)annotation都會(huì)被調(diào)用泉手,比如tableView(_:cellForRowAt:)斩萌,以返回每個(gè)注釋的視圖。
  • 2) 你的應(yīng)用可能會(huì)使用其他的注解颊郎,比如用戶位置憋飞,所以檢查這個(gè)注解是否是一個(gè)Artwork對象。如果不是姆吭,則返回nil以允許地圖視圖使用其默認(rèn)的注釋視圖榛做。
  • 3) 將每個(gè)視圖創(chuàng)建為MKMarkerAnnotationView内狸。在本教程的后面检眯,您將創(chuàng)建MKAnnotationView對象來顯示圖像,而不是標(biāo)記昆淡。
  • 4) 與tableView(_:cellForRowAt:)類似锰瘸,地圖視圖重用了不再可見的注釋視圖。因此昂灵,在創(chuàng)建新注釋視圖之前避凝,要檢查是否有可重用的注釋視圖可用。當(dāng)您將一個(gè)可重用的注釋從隊(duì)列中取出時(shí)眨补,您給它一個(gè)標(biāo)識(shí)符管削。如果您有多種類型的注釋,請確保每種注釋都有唯一的標(biāo)識(shí)符渤涌。這和tableView(_:cellForRowAt:)中的cell標(biāo)識(shí)符是一樣的佩谣。
  • 5) 在這里把还,如果無法將注釋視圖從隊(duì)列中dequeue实蓬,則創(chuàng)建一個(gè)新的MKMarkerAnnotationView對象。它使用Artwork類的標(biāo)題和副標(biāo)題(title and subtitle)屬性來確定在callout中顯示什么吊履。

4. The Map View Delegate

剩下的就是將ViewController設(shè)置為map視圖的代理安皱。你可以在Main.storyboard中這樣做。但如果你在代碼中做艇炎,它更明顯酌伊。在ViewController.swift中,添加這一行到viewDidLoad()缀踪,之前的聲明居砖,創(chuàng)建artwork

mapView.delegate = self

這是它!構(gòu)建和運(yùn)行。點(diǎn)擊標(biāo)記驴娃,彈出callout氣泡:

mapView(_:viewFor:)callout配置為在右側(cè)包含一個(gè)detail disclosure info按鈕奏候,但是點(diǎn)擊該按鈕還沒有完成任何操作。你可以實(shí)現(xiàn)它來顯示一個(gè)包含更多信息的alert或者打開一個(gè)細(xì)節(jié)視圖控制器唇敞。

這是第三種選擇蔗草。當(dāng)用戶點(diǎn)擊信息按鈕時(shí)咒彤,你的應(yīng)用程序就會(huì)啟動(dòng)Maps應(yīng)用程序,完成從模擬用戶位置到藝術(shù)品的駕駛咒精、步行和交通指示镶柱!


Launching the Maps App

為了提供這一偉大的用戶體驗(yàn),打開Artwork.swift并添加這一import聲明:

import Contacts

這將添加Contacts框架模叙,該框架包含字典鍵常量歇拆,如CNPostalAddressStreetKey,用于在需要設(shè)置地址向楼、城市或州字段時(shí)使用查吊。

要告訴Maps應(yīng)用程序要去哪里,您必須傳遞一個(gè)MKMapItem給它湖蜕。這個(gè)類描述地圖上的一個(gè)興趣點(diǎn)逻卖。要?jiǎng)?chuàng)建一個(gè),您必須首先創(chuàng)建一個(gè)MKPlacemark來描述這個(gè)點(diǎn)昭抒。

接下來评也,將以下屬性添加到類中:

var mapItem: MKMapItem? {
  guard let location = locationName else {
    return nil
  }

  let addressDict = [CNPostalAddressStreetKey: location]
  let placemark = MKPlacemark(
    coordinate: coordinate,
    addressDictionary: addressDict)
  let mapItem = MKMapItem(placemark: placemark)
  mapItem.name = title
  return mapItem
}

您可以使用現(xiàn)有的位置信息作為地址來創(chuàng)建MKPlacemark。然后創(chuàng)建并配置與Maps通信所需的MKMapItem灭返。

1. Handling the Callout

接下來盗迟,您必須告訴MapKit當(dāng)用戶點(diǎn)擊callout按鈕時(shí)應(yīng)該做什么。打開ViewController.swift熙含。并將此方法添加到MKMapViewDelegate擴(kuò)展:

func mapView(
  _ mapView: MKMapView,
  annotationView view: MKAnnotationView,
  calloutAccessoryControlTapped control: UIControl
) {
  guard let artwork = view.annotation as? Artwork else {
    return
  }

  let launchOptions = [
    MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving
  ]
  artwork.mapItem?.openInMaps(launchOptions: launchOptions)
}

當(dāng)用戶點(diǎn)擊地圖注釋標(biāo)記時(shí)罚缕,callout顯示一個(gè)info按鈕。如果用戶點(diǎn)擊這個(gè)信息按鈕怎静,iOS會(huì)調(diào)用mapView(_:annotationView: calloutAccessoryControlTap:)邮弹。

在此方法中,獲取此tap引用的Artwork對象蚓聘,然后通過創(chuàng)建關(guān)聯(lián)的MKMapItem并在map item上調(diào)用openInMaps(launchOptions:)來啟動(dòng)Maps腌乡。

注意,您正在向這個(gè)方法傳遞一個(gè)字典夜牡。這允許您指定幾個(gè)不同的選項(xiàng)与纽。這里,DirectionsModeKey設(shè)置為Driving塘装。

這將導(dǎo)致Maps顯示從用戶當(dāng)前位置到此位置的駕駛方向急迂。

注意:查看MKMapItem文檔documentation以查看其他啟動(dòng)選項(xiàng)字典鍵和openMaps(with:launchOptions:)方法,該方法允許您傳遞一個(gè)MKMapItem對象數(shù)組蹦肴。

2. Setting Your Simulated Location

在你構(gòu)建和運(yùn)行之前僚碎,你應(yīng)該移動(dòng)到Honolulu。那么冗尤,只需將您的模擬位置設(shè)置為Honolulu听盖。

Xcode中胀溺,點(diǎn)擊“Product?Scheme?Edit Scheme…”,從左側(cè)菜單中選擇“Run”皆看,然后選擇“Options”選項(xiàng)卡仓坞。選中Allow Location Simulation,并選擇Honolulu, HI腰吟,美國作為默認(rèn)位置无埃。然后點(diǎn)擊Close按鈕:

構(gòu)建和運(yùn)行。你會(huì)看到Waikiki的地圖像以前一樣放大了毛雇。點(diǎn)擊標(biāo)記嫉称,然后點(diǎn)擊callout中的info按鈕,就可以看到它啟動(dòng)Maps灵疮,顯示雕像的位置和前往雕像的駕駛方向:

這需要用你最喜歡的熱帶飲料來慶祝一下!

注意:當(dāng)你第一次打開Maps時(shí)织阅,它會(huì)提示你允許Maps訪問你的位置 —— 點(diǎn)擊Allow While Using App —— 并顯示一個(gè)安全警告。


Decoding GeoJSON with MKGeoJSONDecoder

現(xiàn)在您已經(jīng)知道了如何在地圖上顯示一個(gè)artwork震捣,以及如何從標(biāo)記的callout信息按鈕啟動(dòng)Maps荔棉,現(xiàn)在是時(shí)候?qū)?shù)據(jù)集解析為Artwork對象數(shù)組了。然后將它們作為注釋添加到地圖視圖中蒿赢,以顯示位于當(dāng)前地圖區(qū)域的所有藝術(shù)品润樱。

MapKitMKGeoJSONDecoder,這是一個(gè)非常有用的功能羡棵。它可以解碼GeoJSON數(shù)據(jù)并返回一個(gè)實(shí)現(xiàn)MKGeoJSONObject協(xié)議的對象數(shù)組壹若。MapKit還提供了一個(gè)實(shí)現(xiàn)此協(xié)議的具體類:MKGeoJSONFeature,這是本教程所需的全部內(nèi)容皂冰。

將這個(gè)可失敗的初始化程序添加到Artwork.swift中店展,初始化器下方:

init?(feature: MKGeoJSONFeature) {
  // 1
  guard 
    let point = feature.geometry.first as? MKPointAnnotation,
    let propertiesData = feature.properties,
    let json = try? JSONSerialization.jsonObject(with: propertiesData),
    let properties = json as? [String: Any] 
    else {
      return nil
  }

  // 3
  title = properties["title"] as? String
  locationName = properties["location"] as? String
  discipline = properties["discipline"] as? String
  coordinate = point.coordinate
  super.init()
}

你要做的是:

  • 1) MKGeoJSONFeature具有表示與該特性關(guān)聯(lián)的一個(gè)或多個(gè)形狀的geometry屬性。PublicArt.geojson的所有特點(diǎn)是點(diǎn)位置灼擂,MapKit為您創(chuàng)建了一個(gè)MKPointAnnotation壁查。在這里觉至,您可以找到作為CLLocationCoordinate2D的坐標(biāo)剔应。
  • 2) 接下來,讀取特性的properties语御,哪些是Data?類型并包含一個(gè)序列化的JSON字典峻贮。使用JSONSerialization將數(shù)據(jù)解碼到一個(gè)Swift字典中。
  • 3) 現(xiàn)在已經(jīng)對屬性進(jìn)行了解碼应闯,您可以從字典值中設(shè)置適當(dāng)?shù)?code>Artwork屬性纤控。

1. Making Annotations

要使用這個(gè)初始化器,打開ViewController.swift并添加以下屬性碉纺,一個(gè)數(shù)組來保存來自GeoJSON文件的Artwork對象到類中:

private var artworks: [Artwork] = []

接下來船万,將以下helper方法添加到類中:

private func loadInitialData() {
  // 1
  guard 
    let fileName = Bundle.main.url(forResource: "PublicArt", withExtension: "geojson"),
    let artworkData = try? Data(contentsOf: fileName) 
    else {
      return
  }

  do {
    // 2
    let features = try MKGeoJSONDecoder()
      .decode(artworkData)
      .compactMap { $0 as? MKGeoJSONFeature }
    // 3
    let validWorks = features.compactMap(Artwork.init)
    // 4
    artworks.append(contentsOf: validWorks)
  } catch {
    // 5
    print("Unexpected error: \(error).")
  }
}

以下是你在這段代碼中要做的事情:

  • 1) 首先刻撒,你要讀取PublicArt.geojson轉(zhuǎn)換為Data對象。
  • 2) 使用MKGeoJSONDecoder獲取一個(gè)GeoJSON對象數(shù)組耿导,但僅使用compactMap保存MKGeoJSONFeature的實(shí)例声怔。
  • 3) 使用添加的可失敗初始化器將MKGeoJSONFeature對象轉(zhuǎn)換為Artwork對象,并再次使用compactMap舱呻。
  • 4) 將產(chǎn)生的validWorks添加到artworks數(shù)組中醋火。
  • 5) 因?yàn)?code>MKGeoJSONDecoder的decode(_:)方法會(huì)拋出一個(gè)錯(cuò)誤,所以您可以捕獲它并將錯(cuò)誤打印到Xcode控制臺(tái)箱吕。

Plotting the Artwork

您現(xiàn)在在數(shù)據(jù)集中有了一個(gè)所有公共artwork的數(shù)組芥驳,您將把它添加到地圖中。

還在ViewController.swift中茬高,在viewDidLoad()結(jié)尾添加以下代碼:

loadInitialData()
mapView.addAnnotations(artworks)

注意:一定要使用復(fù)數(shù)形式的addAnnotations兆旬,而不是單數(shù)形式的addAnnotation!

刪除創(chuàng)建單個(gè)King David Kalakaua地圖注釋的行。loadInitialData()創(chuàng)建了artworks數(shù)組怎栽,現(xiàn)在不需要它們了爵憎。

構(gòu)建和運(yùn)行。檢查所有的標(biāo)記婚瓜!

移動(dòng)地圖宝鼓,可以看到其他標(biāo)記出現(xiàn)。點(diǎn)擊一個(gè)標(biāo)記來打開它的callout氣泡巴刻,然后點(diǎn)擊它的信息按鈕來啟動(dòng)Maps愚铡。是的,你對卡拉卡瓦國王雕像所做的一切都與所有新的artwork有關(guān)胡陪!

如果您擔(dān)心在不可見的情況下向地圖添加注釋沥寥,請不要這樣做!蘋果建議立即添加所有注釋Apple recommends adding all the annotations right away柠座,無論它們在地圖區(qū)域是否可見邑雅。當(dāng)您移動(dòng)地圖時(shí),它會(huì)自動(dòng)顯示可見的注釋妈经。

現(xiàn)在淮野,您已經(jīng)構(gòu)建了一個(gè)應(yīng)用程序,該應(yīng)用程序?qū)⒁粋€(gè)GeoJSON文件解析為一個(gè)artworks數(shù)組吹泡,然后將其作為注釋標(biāo)記顯示骤星,并帶有一個(gè)callout信息按鈕來啟動(dòng)Maps

但等一下爆哑,還有一些亮點(diǎn)要添加洞难。


Customizing Annotations

還記得Artwork類中的discipline屬性嗎?它的值比如SculptureMural揭朝。事實(shí)上队贱,最多的disciplineSculpture, Plaque, Mural and Monument色冀。

標(biāo)記的顏色編碼很容易,所以大多數(shù)學(xué)科都有自己的彩色標(biāo)記柱嫌,而所有其他的disciples都有綠色標(biāo)記呐伞。

1. Markers with Color-Coding and Text

Artwork.swift中,添加此屬性:

var markerTintColor: UIColor  {
  switch discipline {
  case "Monument":
    return .red
  case "Mural":
    return .cyan
  case "Plaque":
    return .blue
  case "Sculpture":
    return .purple
  default:
    return .green
  }
}

現(xiàn)在慎式,您可以繼續(xù)向mapView(_:viewFor:)添加代碼伶氢,但這會(huì)使視圖控制器變得混亂。還有一種更優(yōu)雅的方法瘪吏,類似于您可以為table view單元格所做的事情癣防。創(chuàng)建一個(gè)名為ArtworkViews.swift的新Swift文件,并在import語句下面添加以下代碼:

import MapKit

class ArtworkMarkerView: MKMarkerAnnotationView {
  override var annotation: MKAnnotation? {
    willSet {
      // 1
      guard let artwork = newValue as? Artwork else {
        return
      }
      canShowCallout = true
      calloutOffset = CGPoint(x: -5, y: 5)
      rightCalloutAccessoryView = UIButton(type: .detailDisclosure)

      // 2
      markerTintColor = artwork.markerTintColor
      if let letter = artwork.discipline?.first {
        glyphText = String(letter)
      }
    }
  }
}

很快掌眠,您將把這個(gè)類注冊為Artwork注釋的可重用注釋視圖蕾盯。系統(tǒng)將一個(gè)注釋作為newValue傳遞給它,你要做的是:

  • 1) 這些行執(zhí)行與mapView(_:viewFor:)相同的操作蓝丙,配置callout级遭。
  • 2) 然后設(shè)置標(biāo)記的tint顏色,并將其pin圖標(biāo)或字形替換為注釋discipline的第一個(gè)字母渺尘。

2. Color My World

現(xiàn)在切換到ViewController.swift挫鸽。在調(diào)用loadInitialData()之前,將這行代碼添加到viewDidLoad()

mapView.register(
  ArtworkMarkerView.self,
  forAnnotationViewWithReuseIdentifier: 
    MKMapViewDefaultAnnotationViewReuseIdentifier)

在這里鸥跟,您使用地圖視圖的默認(rèn)重用標(biāo)識(shí)符注冊新類丢郊。對于具有更多注釋類型的應(yīng)用程序,可以使用自定義標(biāo)識(shí)符注冊類医咨。

向下滾動(dòng)擴(kuò)展并刪除mapView(_:viewFor:)枫匾。

構(gòu)建和運(yùn)行。然后移動(dòng)地圖拟淮,看看不同的顏色和標(biāo)記標(biāo)記:

在地圖的這一部分干茉,有更多比地圖視圖顯示的藝術(shù)。它通過將過于接近的標(biāo)記聚類來減少混亂很泊。在下一節(jié)中角虫,您將看到所有注釋。

但是首先撑蚌,設(shè)置字形的圖像而不是它的文本上遥。添加以下屬性到Artwork.swift

var image: UIImage {
  guard let name = discipline else { 
    return #imageLiteral(resourceName: "Flag") 
  }

  switch name {
  case "Monument":
    return #imageLiteral(resourceName: "Monument")
  case "Sculpture":
    return #imageLiteral(resourceName: "Sculpture")
  case "Plaque":
    return #imageLiteral(resourceName: "Plaque")
  case "Mural":
    return #imageLiteral(resourceName: "Mural")
  default:
    return #imageLiteral(resourceName: "Flag")
  }
}

這些來自icons8.com的圖片已經(jīng)在Assets.xcassets中了搏屑。

然后争涌,在ArtworkMarkerView.swift,將glyphText行替換為:

glyphImage = artwork.image

構(gòu)建和運(yùn)行看到不同的彩色標(biāo)記與圖像:

這是另一個(gè)定制選項(xiàng)的segue你的下一個(gè)任務(wù):用圖像替換標(biāo)記辣恋!

3. Annotations with Images

ArtworkViews.swift亮垫,添加以下類:

class ArtworkView: MKAnnotationView {
  override var annotation: MKAnnotation? {
    willSet {
      guard let artwork = newValue as? Artwork else {
        return
      }

      canShowCallout = true
      calloutOffset = CGPoint(x: -5, y: 5)
      rightCalloutAccessoryView = UIButton(type: .detailDisclosure)

      image = artwork.image
    }
  }
}

現(xiàn)在模软,您使用的是一個(gè)普通的舊MKAnnotationView,而不是MKMarkerAnnotationView饮潦,而且該視圖有一個(gè)image屬性燃异。

回到ViewController.swift中。在viewDidLoad()中继蜡,注冊這個(gè)新類回俐,而不是ArtworkMarkerView

mapView.register(
  ArtworkView.self,
  forAnnotationViewWithReuseIdentifier: 
    MKMapViewDefaultAnnotationViewReuseIdentifier)

構(gòu)建并運(yùn)行以看見所有的圖標(biāo)

4. Custom Callout Accessory Views

右邊的callout附件是一個(gè)信息按鈕,但是點(diǎn)擊它會(huì)打開Maps∠〔ⅲ現(xiàn)在仅颇,您將更改按鈕以顯示Maps圖標(biāo)。

ArtworkView中找到這一行:

rightCalloutAccessoryView = UIButton(type: .detailDisclosure)

用以下代碼替換這一行:

let mapsButton = UIButton(frame: CGRect(
  origin: CGPoint.zero, 
  size: CGSize(width: 48, height: 48)))
mapsButton.setBackgroundImage(#imageLiteral(resourceName: "Map"), for: .normal)
rightCalloutAccessoryView = mapsButton

這里碘举,你創(chuàng)建一個(gè)UIButton忘瓦,設(shè)置它的背景圖像為一個(gè)地圖圖標(biāo),也來自于Assets.xcassets中的icons8.com引颈。然后將視圖的右側(cè)callout附件設(shè)置為此按鈕耕皮。

構(gòu)建和運(yùn)行。然后點(diǎn)擊一個(gè)視圖來查看新的Maps按鈕:

最后的定制是detail callout附件蝙场。這是一個(gè)單一的行凌停,這是足夠短的位置文本,但一些較長的位置值被截?cái)嗳缦?

現(xiàn)在您需要一個(gè)多行標(biāo)簽售滤。添加以下代碼到ArtworkViewwillSet:

let detailLabel = UILabel()
detailLabel.numberOfLines = 0
detailLabel.font = detailLabel.font.withSize(12)
detailLabel.text = artwork.subtitle
detailCalloutAccessoryView = detailLabel

構(gòu)建和運(yùn)行苦锨。然后點(diǎn)擊一個(gè)視圖來查看完整的長位置文本。

現(xiàn)在您已經(jīng)了解了使用MapKit的基礎(chǔ)知識(shí)趴泌,但是您還可以添加更多內(nèi)容:地圖顯示自定義舟舒、地理編碼、地理圍欄嗜憔、自定義地圖覆蓋等等秃励。 MapKit documentationLocation and Maps Programming Guide是查找更多信息的好地方吉捶。

還可以看看WWDC 2019 Session 236: What’s New in MapKit and MapKit JS夺鲜,以發(fā)現(xiàn)更多的酷功能添加到iOS 13中。

后記

本篇主要介紹了地圖特定區(qū)域放大和創(chuàng)建自定義地圖annotations呐舔,感興趣的給個(gè)贊或者關(guān)注~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末币励,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子珊拼,更是在濱河造成了極大的恐慌食呻,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異仅胞,居然都是意外死亡每辟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門干旧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來渠欺,“玉大人,你說我怎么就攤上這事椎眯∧咏” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵编整,是天一觀的道長捐名。 經(jīng)常有香客問我,道長闹击,這世上最難降的妖魔是什么镶蹋? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮赏半,結(jié)果婚禮上贺归,老公的妹妹穿的比我還像新娘。我一直安慰自己断箫,他們只是感情好拂酣,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著仲义,像睡著了一般婶熬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上埃撵,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天赵颅,我揣著相機(jī)與錄音,去河邊找鬼暂刘。 笑死饺谬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的谣拣。 我是一名探鬼主播募寨,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼森缠!你這毒婦竟也來了拔鹰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤贵涵,失蹤者是張志新(化名)和其女友劉穎列肢,沒想到半個(gè)月后恰画,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡例书,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年锣尉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了刻炒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片决采。...
    茶點(diǎn)故事閱讀 39,919評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖坟奥,靈堂內(nèi)的尸體忽然破棺而出树瞭,到底是詐尸還是另有隱情,我是刑警寧澤爱谁,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布晒喷,位于F島的核電站,受9級(jí)特大地震影響访敌,放射性物質(zhì)發(fā)生泄漏凉敲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一寺旺、第九天 我趴在偏房一處隱蔽的房頂上張望爷抓。 院中可真熱鬧,春花似錦阻塑、人聲如沸蓝撇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渤昌。三九已至,卻和暖如春走搁,著一層夾襖步出監(jiān)牢的瞬間独柑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工私植, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留群嗤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓兵琳,卻偏偏與公主長得像狂秘,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子躯肌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容