版本記錄
版本號(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
MapKit
是iOS
設(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
的值是類型為Feature
的GeoJSON
對象數(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
的青銅雕像胳施。
這個(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)題惧浴,那么您的類還需要名為title
和subtitle
的屬性衷旅。
Artwork
類完全可以存儲(chǔ)名為title
和coordinate
的屬性柿顶,而不存儲(chǔ)PublicArt.geojson
屬性自然地映射到subtitle
嘁锯。要符合MKAnnotation
,可以將subtitle
作為返回locationName
的計(jì)算屬性仁锯。
MKAnnotation
協(xié)議屬性title
和subtitle
被定義為可選的字符串业崖,但是您可能想知道為什么要使用String?
作為locationName
和discipline
屬性的類型腻要。由于這些屬性是從外部數(shù)據(jù)源PublicArt.geojson
設(shè)置文件雄家,你不能保證它們總是存在乱投。最好是安全一點(diǎn)戚炫。
你會(huì)使用MKAnnotation
對象的title双肤,locationName
和coordinate
屬性,但它的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ù)品润樱。
MapKit
有MKGeoJSONDecoder
,這是一個(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
屬性嗎?它的值比如Sculpture
和 Mural
揭朝。事實(shí)上队贱,最多的discipline
是Sculpture, 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)簽售滤。添加以下代碼到ArtworkView
的willSet
:
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 documentation、Location 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)注~~~