MapKit框架詳細(xì)解析(十三) —— MapKit Overlay Views(一)

版本記錄

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

前言

MapKit框架直接從您的應(yīng)用界面顯示地圖或衛(wèi)星圖像,調(diào)出興趣點(diǎn)墩朦,并確定地圖坐標(biāo)的地標(biāo)信息。接下來(lái)幾篇我們就一起看一下這個(gè)框架筒严。感興趣的看下面幾篇文章擒滑。
1. MapKit框架詳細(xì)解析(一) —— 基本概覽(一)
2. MapKit框架詳細(xì)解析(二) —— 基本使用簡(jiǎn)單示例(一)
3. MapKit框架詳細(xì)解析(三) —— 基本使用簡(jiǎn)單示例(二)
4. MapKit框架詳細(xì)解析(四) —— 一個(gè)疊加視圖相關(guān)的簡(jiǎn)單示例(一)
5. MapKit框架詳細(xì)解析(五) —— 一個(gè)疊加視圖相關(guān)的簡(jiǎn)單示例(二)
6. MapKit框架詳細(xì)解析(六) —— 添加自定義圖塊(一)
7. MapKit框架詳細(xì)解析(七) —— 添加自定義圖塊(二)
8. MapKit框架詳細(xì)解析(八) —— 添加自定義圖塊(三)
9. MapKit框架詳細(xì)解析(九) —— 地圖特定區(qū)域放大和創(chuàng)建自定義地圖annotations(一)
10. MapKit框架詳細(xì)解析(十) —— 地圖特定區(qū)域放大和創(chuàng)建自定義地圖annotations(二)
11. MapKit框架詳細(xì)解析(十一) —— 自定義MapKit Tiles(一)
12. MapKit框架詳細(xì)解析(十二) —— 自定義MapKit Tiles(二)

開(kāi)始

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

在本MapKit Overlay教程中,您將學(xué)習(xí)如何在原生iOS地圖上繪制圖像和線(xiàn)條称勋,以使其對(duì)用戶(hù)更具交互性胸哥。本文內(nèi)容來(lái)自翻譯涯竟。

下面看一下寫(xiě)作環(huán)境:

Swift 5, iOS 13, Xcode 11

下面就是正文了赡鲜。

雖然MapKit可以輕松地將地圖添加到您的應(yīng)用程序中,但是僅靠這一點(diǎn)并不是很吸引人庐船。 幸運(yùn)的是银酬,您可以使用自定義疊加視圖(custom overlay views)來(lái)制作更具吸引力的地圖。

在此MapKit教程中筐钟,您將創(chuàng)建一個(gè)展示Six Flags Magic Mountain的應(yīng)用揩瞪。 完成后,您將獲得一個(gè)交互式的公園地圖篓冲,其中顯示了景點(diǎn)李破,乘車(chē)路線(xiàn)和角色位置。 這個(gè)程序適合所有您在那里快速尋求刺激的人壹将。

在Xcode中打開(kāi)入門(mén)項(xiàng)目嗤攻。

入門(mén)項(xiàng)目包括您將要使用的地圖以及用于打開(kāi)和關(guān)閉不同類(lèi)型疊加層(overlays)的按鈕。

Build并運(yùn)行诽俯。 您會(huì)看到以下內(nèi)容:


All About Overlay Views

在開(kāi)始創(chuàng)建疊加視圖(overlay views)之前妇菱,您需要了解兩個(gè)關(guān)鍵類(lèi):MKOverlayMKOverlayRenderer

MKOverlay告訴MapKit您希望它在何處繪制疊加層暴区。使用此類(lèi)的三個(gè)步驟:

  • 1) 首先闯团,創(chuàng)建實(shí)現(xiàn)MKOverlay協(xié)議protocol的自定義類(lèi),該類(lèi)具有兩個(gè)必需的屬性:coordinateboundingMapRect仙粱。這些屬性定義了疊加層在地圖上的位置及其大小房交。
  • 2) 然后,為要顯示疊加層的每個(gè)區(qū)域創(chuàng)建類(lèi)的實(shí)例伐割。例如候味,在此應(yīng)用中淹遵,您將為過(guò)山車(chē)疊加層創(chuàng)建一個(gè)實(shí)例,為餐廳疊加層創(chuàng)建一個(gè)實(shí)例负溪。
  • 3) 最后透揣,將疊加層添加到地圖視圖中。

此時(shí)川抡,地圖知道應(yīng)該在哪里顯示疊加層辐真。但是它不知道在每個(gè)區(qū)域顯示什么。

這就是MKOverlayRenderer的作用崖堤。對(duì)其進(jìn)行子類(lèi)化可以設(shè)置要在每個(gè)位置顯示的內(nèi)容侍咱。

例如,在此應(yīng)用中密幔,您將繪制過(guò)山車(chē)或餐廳的圖像楔脯。 MapKit期望提供一個(gè)MKMapView對(duì)象胯甩,并且此類(lèi)定義地圖視圖使用的繪圖基礎(chǔ)結(jié)構(gòu)昧廷。

看一下入門(mén)項(xiàng)目。在ContentView.swift中偎箫,您將看到一個(gè)代理方法木柬,該方法可讓您返回疊加視圖(overlay view)

func mapView(
  _ mapView: MKMapView, 
  rendererFor overlay: MKOverlay
) -> MKOverlayRenderer

當(dāng)MapKit意識(shí)到在地圖視圖顯示的區(qū)域中存在MKOverlay對(duì)象時(shí),MapKit會(huì)調(diào)用此方法淹办。

綜上所述眉枕,您無(wú)需將MKOverlayRenderer對(duì)象直接添加到地圖視圖中。 取而代之的是怜森,您告訴地圖有關(guān)要顯示的MKOverlay對(duì)象的信息速挑,并在委托方法請(qǐng)求它們時(shí)返回MKOverlayRenderers

現(xiàn)在副硅,您已經(jīng)了解了理論姥宝,是時(shí)候使用這些概念了!


Adding Your Information to the Map

目前想许,該地圖無(wú)法提供有關(guān)公園的足夠信息伶授。 您的任務(wù)是創(chuàng)建一個(gè)代表整個(gè)公園的疊加層的對(duì)象。

首先流纹,選擇Overlays組糜烹,然后創(chuàng)建一個(gè)名為ParkMapOverlay.swift的新Swift文件。 然后將其內(nèi)容替換為:

import MapKit

class ParkMapOverlay: NSObject, MKOverlay {
  let coordinate: CLLocationCoordinate2D
  let boundingMapRect: MKMapRect
  
  init(park: Park) {
    boundingMapRect = park.overlayBoundingMapRect
    coordinate = park.midCoordinate
  }
}

符合MKOverlay會(huì)強(qiáng)制您從NSObject繼承漱凝。 初始化程序從傳遞的Park對(duì)象(已在入門(mén)項(xiàng)目中)獲取屬性疮蹦,并將其設(shè)置為相應(yīng)的MKOverlay屬性。

接下來(lái)茸炒,您需要?jiǎng)?chuàng)建一個(gè)MKOverlayRenderer愕乎,它知道如何繪制此疊加層阵苇。

Overlays組中創(chuàng)建一個(gè)名為ParkMapOverlayView.swift的新Swift文件。 將其內(nèi)容替換為:

import MapKit

class ParkMapOverlayView: MKOverlayRenderer {
  let overlayImage: UIImage
  
  // 1
  init(overlay: MKOverlay, overlayImage: UIImage) {
    self.overlayImage = overlayImage
    super.init(overlay: overlay)
  }
  
  // 2
  override func draw(
    _ mapRect: MKMapRect, 
    zoomScale: MKZoomScale, 
    in context: CGContext
  ) {
    guard let imageReference = overlayImage.cgImage else { return }
    
    let rect = self.rect(for: overlay.boundingMapRect)
    context.scaleBy(x: 1.0, y: -1.0)
    context.translateBy(x: 0.0, y: -rect.size.height)
    context.draw(imageReference, in: rect)
  }
}

以下是您添加的內(nèi)容的細(xì)分:

  • 1) init(overlay:overlayImage :)通過(guò)提供第二個(gè)參數(shù)來(lái)覆蓋基本方法init(overlay :)感论。
  • 2) draw(_:zoomScale:in :)是此類(lèi)的真實(shí)內(nèi)容绅项。 它定義了MapKit在給定特定MKMapRectMKZoomScale和圖形上下文的CGContext時(shí)應(yīng)如何呈現(xiàn)此視圖比肄,目的是以適當(dāng)?shù)谋壤龑⒏采w圖像(overlay image)繪制到上下文上快耿。

注意:Core Graphics繪圖的詳細(xì)信息不在本教程的討論范圍之內(nèi)。 但是芳绩,您可以看到上面的代碼使用傳遞的MKMapRect來(lái)獲取一個(gè)CGRect掀亥,以便在其中提供在所提供的上下文中繪制圖像。

很好妥色,現(xiàn)在您已經(jīng)有了MKOverlayMKOverlayRenderer搪花,將它們添加到地圖視圖中。


Creating Your First Map Overlay

ContentView.swift中嘹害,找到addOverlay()并將其TODO內(nèi)容更改為:

let overlay = ParkMapOverlay(park: park)
mapView.addOverlay(overlay)

此方法將ParkMapOverlay添加到地圖視圖(map view)撮竿。

看一下updateMapOverlayViews()。 您會(huì)看到吼拥,當(dāng)用戶(hù)點(diǎn)擊導(dǎo)航欄中的按鈕以顯示地圖疊加層時(shí)倚聚,就會(huì)調(diào)用addOverlay()。 現(xiàn)在凿可,您已經(jīng)添加了必要的代碼,隨即顯示疊加層授账。

請(qǐng)注意枯跑,updateMapOverlayViews()還刪除了可能存在的所有注釋和疊加層(annotations and overlays),因此您不會(huì)得到重復(fù)的渲染白热。 這不一定有效敛助,但這是一種從地圖上清除先前項(xiàng)目的簡(jiǎn)單方法。

站在您與您在地圖上看到新實(shí)現(xiàn)的疊加層之間的最后一步是前面提到的mapView(_:rendererFor :)屋确。 將其當(dāng)前的TODO實(shí)施替換為:

if overlay is ParkMapOverlay {
  return ParkMapOverlayView(
    overlay: overlay, 
    overlayImage: UIImage(imageLiteralResourceName: "overlay_park"))
}

當(dāng)MapKit確定MKOverlay在視圖中時(shí)纳击,它將調(diào)用此委托方法以獲得渲染器。

在這里攻臀,您可以檢查疊加層是否為ParkMapOverlay類(lèi)類(lèi)型焕数。 如果是這樣,則加載疊加圖像刨啸,使用疊加圖像創(chuàng)建一個(gè)ParkMapOverlayView實(shí)例堡赔,然后將此實(shí)例返回給調(diào)用者。

不過(guò)设联,這里缺少一小塊:可疑的overlay_park小圖像是從哪里來(lái)的善已? 這是將地圖與定義的公園邊界疊加在一起的PNG灼捂。 在Assets.xcassets中找到的overlay_park圖像如下所示:

構(gòu)建并運(yùn)行,啟用屏幕頂部的:Overlay:選項(xiàng)换团,然后加油悉稠! 這是在地圖頂部繪制的公園疊加層:

放大,縮小并四處移動(dòng)艘包。 覆蓋層會(huì)按預(yù)期縮放和移動(dòng)偎球。Cool!


Adding Annotations

如果您曾經(jīng)在原生Maps應(yīng)用中搜索過(guò)某個(gè)位置辑甜,那么您會(huì)看到這些彩色圖釘(pins)出現(xiàn)在地圖上衰絮。 這些是使用MKAnnotationView創(chuàng)建的注釋(annotations)。 您可以在自己的應(yīng)用程序中使用注釋?zhuān)⑹褂盟璧娜魏螆D像磷醋,而不僅僅是pins`猫牡!

Annotations有助于突出顯示公園游客的特定興趣點(diǎn)。 它們的工作方式類(lèi)似于MKOverlayMKOverlayRenderer邓线,但是您將使用MKAnnotationMKAnnotationView淌友。

1. Writing Your First Annotation

首先,在Annotations組中創(chuàng)建一個(gè)名為AttractionAnnotation.swift的新Swift文件骇陈。 然后震庭,將其內(nèi)容替換為:

import MapKit

// 1
enum AttractionType: Int {
  case misc = 0
  case ride
  case food
  case firstAid
  
  func image() -> UIImage {
    switch self {
    case .misc:
      return UIImage(imageLiteralResourceName: "star")
    case .ride:
      return UIImage(imageLiteralResourceName: "ride")
    case .food:
      return UIImage(imageLiteralResourceName: "food")
    case .firstAid:
      return UIImage(imageLiteralResourceName: "firstaid")
    }
  }
}

// 2
class AttractionAnnotation: NSObject, MKAnnotation {
  // 3
  let coordinate: CLLocationCoordinate2D
  let title: String?
  let subtitle: String?
  let type: AttractionType
  
  // 4
  init(
    coordinate: CLLocationCoordinate2D,
    title: String,
    subtitle: String,
    type: AttractionType
  ) {
    self.coordinate = coordinate
    self.title = title
    self.subtitle = subtitle
    self.type = type
  }
}

這是您添加的內(nèi)容:

  • 1) AttractionType可幫助您將每個(gè)景點(diǎn)分類(lèi)為一種類(lèi)型。 該枚舉列出了四種類(lèi)型的注釋?zhuān)弘s項(xiàng)你雌,乘車(chē)器联,食物和急救(misc, rides, foods and first aid)。 還有一種方便的方法來(lái)獲取正確的annotation圖像婿崭。
  • 2) 您創(chuàng)建此類(lèi)并使其符合 MKAnnotation拨拓。
  • 3) 與MKOverlay非常相似,MKAnnotation具有必需的coordinate屬性氓栈。 您定義了一些特定于此實(shí)現(xiàn)的屬性渣磷。
  • 4) 最后,定義一個(gè)初始化程序授瘦,該初始化程序可讓您為每個(gè)屬性分配值醋界。

接下來(lái),您將創(chuàng)建一個(gè)MKAnnotationView的特定實(shí)例以用于您的annotations提完。

2. Associating a View With Your Annotation

首先形纺,在Annotations組中創(chuàng)建另一個(gè)名為AttractionAnnotationView.swiftSwift文件。 然后氯葬,將其內(nèi)容替換為以下代碼段:

import MapKit

class AttractionAnnotationView: MKAnnotationView {
  // 1
  // Required for MKAnnotationView
  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
  }
  
  // 2
  override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
    super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
    guard 
      let attractionAnnotation = self.annotation as? AttractionAnnotation else { 
        return 
    }
    
    image = attractionAnnotation.type.image()
  }
}

以下是代碼細(xì)分:

  • 1) MKAnnotationView需要使用init(coder :)挡篓。 如果沒(méi)有定義,錯(cuò)誤將阻止您構(gòu)建和運(yùn)行應(yīng)用程序。 為了避免這種情況官研,請(qǐng)定義它并調(diào)用其超類(lèi)初始化程序秽澳。
  • 2) 您還可以覆蓋init(annotation:reuseIdentifier :)并根據(jù)annotationtype屬性設(shè)置其他注釋(annotation)圖像。

創(chuàng)建annotation及其相關(guān)視圖之后戏羽,就可以開(kāi)始向地圖視圖添加annotation了担神!


Adding Annotations to the Map

要確定每個(gè)annotation的位置,請(qǐng)使用MagicMountainAttractions.plist文件中的信息始花,該文件位于Park Information組下妄讯。plist文件包含有關(guān)公園景點(diǎn)的坐標(biāo)信息和其他詳細(xì)信息。

返回ContentView.swift并將TODO:addAttractionPins()的實(shí)現(xiàn)替換為:

// 1
guard let attractions = Park.plist("MagicMountainAttractions") 
  as? [[String: String]] else { return }

// 2
for attraction in attractions {
  let coordinate = Park.parseCoord(dict: attraction, fieldName: "location")
  let title = attraction["name"] ?? ""
  let typeRawValue = Int(attraction["type"] ?? "0") ?? 0
  let type = AttractionType(rawValue: typeRawValue) ?? .misc
  let subtitle = attraction["subtitle"] ?? ""
  // 3
  let annotation = AttractionAnnotation(
    coordinate: coordinate, 
    title: title, 
    subtitle: subtitle, 
    type: type)
  mapView.addAnnotation(annotation)
}

以下是分步細(xì)分:

  • 1) 首先酷宵,您閱讀MagicMountainAttractions.plist并將其存儲(chǔ)為字典數(shù)組亥贸。
  • 2) 然后,您遍歷數(shù)組中的每個(gè)字典浇垦。
  • 3) 對(duì)于每個(gè)條目炕置,您都將創(chuàng)建一個(gè)AttractionAnnotation實(shí)例以及該點(diǎn)的信息,并將其添加到地圖視圖中男韧。

最后但并非最不重要的一點(diǎn)是朴摊,您需要實(shí)現(xiàn)另一個(gè)委托方法,該方法將MKAnnotationView實(shí)例提供給地圖視圖此虑,以便它可以自行渲染它們甚纲。

將以下方法添加到文件頂部的Coordinator類(lèi)中:

func mapView(
  _ mapView: MKMapView, 
  viewFor annotation: MKAnnotation
) -> MKAnnotationView? {
  let annotationView = AttractionAnnotationView(
    annotation: annotation, 
    reuseIdentifier: "Attraction")
  annotationView.canShowCallout = true
  return annotationView
}

此方法接收選定的MKAnnotation并使用它來(lái)創(chuàng)建AttractionAnnotationView。 由于canShowCallout屬性設(shè)置為true朦前,因此當(dāng)用戶(hù)觸摸注釋(annotation)時(shí)會(huì)出現(xiàn)一個(gè)標(biāo)注(call-out)介杆。 最后,該方法返回注釋視圖(annotation view)况既。

Build并運(yùn)行以查看實(shí)際中的annotations这溅! 不要忘記打開(kāi):Pins:選項(xiàng)。

在這一點(diǎn)上棒仍,Attraction pins看起來(lái)相當(dāng)鋒利!

到目前為止臭胜,您已經(jīng)介紹了MapKit的一些復(fù)雜部分莫其,包括疊加層和注釋(overlays and annotations)。 但是耸三,如果您需要使用一些繪圖圖元(如直線(xiàn)和圓)怎么辦乱陡?

MapKit框架還允許您直接繪制到地圖視圖上。 MapKit為此提供了MKPolyline仪壮,MKPolygonMKCircle憨颠。 是時(shí)候嘗試一下了!


I Walk The Line: MKPolyline

如果您去過(guò)魔術(shù)山(Magic Mountain),您就會(huì)知道歌利亞(Goliath)過(guò)山車(chē)是一次不可思議的旅程爽彤。 一些車(chē)手喜歡在走入大門(mén)時(shí)就做出一條直線(xiàn)养盗!

為了幫助這些騎手,您將畫(huà)一條從公園入口到巨人的路徑适篙。

MKPolyline是繪制連接多個(gè)點(diǎn)的路徑的絕佳解決方案往核,例如繪制從點(diǎn)A到點(diǎn)B的非線(xiàn)性路線(xiàn)。

要繪制折線(xiàn)(polyline)嚷节,您需要按照繪制順序繪制一系列經(jīng)度和緯度坐標(biāo)聂儒。 再次在Park Information文件夾中找到的EntranceToGoliathRoute.plist包含路徑信息。

現(xiàn)在硫痰,您需要一種方法來(lái)讀取該plist文件并創(chuàng)建供騎手遵循的路線(xiàn)衩婚。

首先,打開(kāi)ContentView.swift并找到addRoute()效斑。 然后非春,將其當(dāng)前的TODO實(shí)施替換為:

guard let points = Park.plist("EntranceToGoliathRoute") as? [String] else { 
  return 
}
    
let cgPoints = points.map { NSCoder.cgPoint(for: $0) }
let coords = cgPoints.map { CLLocationCoordinate2D(
  latitude: CLLocationDegrees($0.x), 
  longitude: CLLocationDegrees($0.y))
}
let myPolyline = MKPolyline(coordinates: coords, count: coords.count)
    
mapView.addOverlay(myPolyline)

此方法讀取EntranceToGoliathRoute.plist并將單個(gè)坐標(biāo)字符串轉(zhuǎn)換為CLLocationCoordinate2D結(jié)構(gòu)。

實(shí)現(xiàn)折線(xiàn)非常簡(jiǎn)單:您只需創(chuàng)建一個(gè)包含所有點(diǎn)的數(shù)組鳍悠,然后將其傳遞給MKPolyline税娜! 沒(méi)有比這容易的多了。

請(qǐng)記住藏研,每當(dāng)用戶(hù)通過(guò)UI切換此選項(xiàng)時(shí)敬矩,updateMapOverlayViews()已經(jīng)調(diào)用addRoute()。 現(xiàn)在剩下的就是讓您更新委托方法蠢挡,以便它返回要在地圖視圖上呈現(xiàn)的實(shí)際視圖弧岳。

返回mapView(_:rendererFor :)并將此else if子句添加到現(xiàn)有條件中:

else if overlay is MKPolyline {
  let lineView = MKPolylineRenderer(overlay: overlay)
  lineView.strokeColor = .green
  return lineView
}

顯示折線(xiàn)視圖的過(guò)程與以前的疊加視圖非常相似。 但是业踏,在這種情況下禽炬,您無(wú)需創(chuàng)建任何自定義視圖對(duì)象。 您只需使用提供的MKPolyLineRenderer類(lèi)并使用疊加層(overlay)初始化一個(gè)新實(shí)例勤家。

MKPolyLineRenderer還可讓您更改某些折線(xiàn)的屬性腹尖。 在這種情況下,您已經(jīng)修改了筆觸顏色以顯示為綠色伐脖。

Build并運(yùn)行您的應(yīng)用程序热幔。 啟用:Route:選項(xiàng),它會(huì)顯示在屏幕上:

現(xiàn)在讼庇,Goliath fanatics可以在創(chuàng)紀(jì)錄的時(shí)間內(nèi)登上過(guò)山車(chē)绎巨!

最好向公園顧客顯示公園邊界,因?yàn)楣珗@實(shí)際上并沒(méi)有占據(jù)屏幕上顯示的整個(gè)空間蠕啄。

您可以使用MKPolyline在公園邊界周?chē)L制形狀场勤,但是MapKit提供了另一個(gè)專(zhuān)門(mén)設(shè)計(jì)用于繪制閉合多邊形的類(lèi):MKPolygon


Don’t Fence Me In: MKPolygon

MKPolygonMKPolyline相似,不同之處在于坐標(biāo)集中的第一個(gè)點(diǎn)和最后一個(gè)點(diǎn)相互連接以創(chuàng)建閉合形狀和媳。

您將創(chuàng)建一個(gè)MKPolygon作為顯示公園邊界的疊加層格遭。 公園邊界坐標(biāo)在MagicMountain.plist中定義。 查看Park.swift中的init(filename :)以查看從plist文件讀取邊界點(diǎn)的位置窗价。

現(xiàn)在如庭,在ContentView.swift中,將addBoundary()TODO實(shí)現(xiàn)替換為:

mapView.addOverlay(MKPolygon(
  coordinates: park.boundary, 
  count: park.boundary.count))

給定公園實(shí)例的邊界數(shù)組和點(diǎn)數(shù)撼港,您可以快速輕松地創(chuàng)建一個(gè)新的MKPolygon實(shí)例坪它!

你能猜到下一步嗎? 與您對(duì)MKPolyline所做的類(lèi)似帝牡。

是的往毡,沒(méi)錯(cuò)。 MKPolygonMKPolyline一樣符合MKOverlay靶溜,因此您需要再次更新委托方法开瞭。

返回mapView(_:rendererFor :)并將此else if子句添加到現(xiàn)有條件中:

else if overlay is MKPolygon {
  let polygonView = MKPolygonRenderer(overlay: overlay)
  polygonView.strokeColor = .magenta
  return polygonView
}

您創(chuàng)建一個(gè)MKOverlayView作為MKPolygonRenderer的實(shí)例,并將stroke顏色設(shè)置為洋紅色罩息。

運(yùn)行應(yīng)用程序并啟用:Bound:選項(xiàng)嗤详,以查看新邊界的實(shí)際作用。 您可能需要縮小以使公園邊界適合模擬器的屏幕邊界瓷炮。

這會(huì)考慮到折線(xiàn)和多邊形(polylines and polygons)葱色。 涉及到的最后一種繪制方法是繪制圓圈作為疊加層,您將使用MKCircle進(jìn)行繪制娘香。


Circle in the Sand: MKCircle

MKCircle也非常類(lèi)似于MKPolylineMKPolygon苍狰,不同之處在于,在給定中心坐標(biāo)點(diǎn)和確定圓弧大小的半徑時(shí)烘绽,它會(huì)繪制一個(gè)圓淋昭。

許多公園游客喜歡與人物一起參觀。 您可以通過(guò)用圓圈標(biāo)記最后在地圖上發(fā)現(xiàn)characters的位置來(lái)幫助他們找到字符安接。 MKCircle疊加層使您可以輕松地執(zhí)行此操作翔忽。

Park Information文件夾還包含角色位置文件。 每個(gè)文件都是由幾個(gè)坐標(biāo)組成的數(shù)組盏檐,用戶(hù)可以在其中發(fā)現(xiàn)角色呀打。

首先,在Models組下創(chuàng)建一個(gè)名為Character.swift的新swift文件糯笙。 然后,將其內(nèi)容替換為以下代碼:

import MapKit

// 1
class Character: MKCircle {
  // 2
  private var name: String?
  var color: UIColor?
  
  // 3
  convenience init(filename: String, color: UIColor) {
    guard let points = Park.plist(filename) as? [String] else { 
      self.init()
      return
    }
    
    let cgPoints = points.map { NSCoder.cgPoint(for: $0) }
    let coords = cgPoints.map {
      CLLocationCoordinate2D(
        latitude: CLLocationDegrees($0.x), 
        longitude: CLLocationDegrees($0.y))
    }
    
    let randomCenter = coords[Int.random(in: 0...3)]
    let randomRadius = CLLocationDistance(Int.random(in: 5...39))
    
    self.init(center: randomCenter, radius: randomRadius)
    self.name = filename
    self.color = color
  }
}

此代碼的作用如下:

  • 1) Character類(lèi)符合MKCircle協(xié)議撩银。
  • 2) 它定義了兩個(gè)可選屬性:namecolor给涕。
  • 3) 便捷初始化程序接受plist文件名和顏色來(lái)繪制圓圈。 然后,它從plist文件中讀取數(shù)據(jù)够庙,并從文件的四個(gè)位置中選擇一個(gè)隨機(jī)位置恭应。 接下來(lái),它選擇一個(gè)隨機(jī)半徑來(lái)模擬時(shí)間變化耘眨。 返回的MKCircle已設(shè)置好昼榛,可以放到地圖上了!

現(xiàn)在剔难,您需要一種添加characters的方法胆屿。 因此,打開(kāi)ContentView.swift并將addCharacterLocation()TODO實(shí)現(xiàn)替換為:

mapView.addOverlay(Character(filename: "BatmanLocations", color: .blue))
mapView.addOverlay(Character(filename: "TazLocations", color: .orange))
mapView.addOverlay(Character(filename: "TweetyBirdLocations", color: .yellow))

該方法對(duì)每個(gè)character執(zhí)行幾乎相同的操作:它為每個(gè)字符傳遞plist文件名偶宫,確定顏色并將其作為覆蓋添加到地圖上非迹。

你幾乎完成! 你還記得最后一步嗎纯趋?

對(duì)憎兽! 您需要使用委托方法為地圖視圖提供MKOverlayView

返回mapView(_:rendererFor :)并將此else if子句添加到現(xiàn)有條件中:

else if let character = overlay as? Character {
  let circleView = MKCircleRenderer(overlay: character)
  circleView.strokeColor = character.color
  return circleView
}

Build并運(yùn)行該應(yīng)用程序吵冒,然后啟用:Characters:選項(xiàng)以查看每個(gè)人都隱藏在哪里纯命!

有許多更高級(jí),甚至更有效的方法來(lái)創(chuàng)建overlays痹栖。 例如亿汞,您可以使用KML tiles或其他第三方提供的資源。

后記

本篇主要講述了Overlay Views结耀,感興趣的給個(gè)贊或者關(guān)注~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末留夜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子图甜,更是在濱河造成了極大的恐慌碍粥,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,561評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件黑毅,死亡現(xiàn)場(chǎng)離奇詭異嚼摩,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)矿瘦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)枕面,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人缚去,你說(shuō)我怎么就攤上這事潮秘。” “怎么了易结?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,162評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵枕荞,是天一觀的道長(zhǎng)柜候。 經(jīng)常有香客問(wèn)我,道長(zhǎng)躏精,這世上最難降的妖魔是什么渣刷? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,470評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮矗烛,結(jié)果婚禮上辅柴,老公的妹妹穿的比我還像新娘。我一直安慰自己瞭吃,他們只是感情好碌嘀,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著虱而,像睡著了一般筏餐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上牡拇,一...
    開(kāi)封第一講書(shū)人閱讀 49,806評(píng)論 1 290
  • 那天魁瞪,我揣著相機(jī)與錄音,去河邊找鬼惠呼。 笑死导俘,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的剔蹋。 我是一名探鬼主播旅薄,決...
    沈念sama閱讀 38,951評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼泣崩!你這毒婦竟也來(lái)了少梁?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,712評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤矫付,失蹤者是張志新(化名)和其女友劉穎凯沪,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體买优,經(jīng)...
    沈念sama閱讀 44,166評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡妨马,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了杀赢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烘跺。...
    茶點(diǎn)故事閱讀 38,643評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖脂崔,靈堂內(nèi)的尸體忽然破棺而出滤淳,到底是詐尸還是另有隱情,我是刑警寧澤砌左,帶...
    沈念sama閱讀 34,306評(píng)論 4 330
  • 正文 年R本政府宣布娇钱,位于F島的核電站伤柄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏文搂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評(píng)論 3 313
  • 文/蒙蒙 一秤朗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦忽孽、人聲如沸顷窒。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,745評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)稽物。三九已至,卻和暖如春折欠,著一層夾襖步出監(jiān)牢的瞬間贝或,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,983評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工锐秦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留咪奖,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,351評(píng)論 2 360
  • 正文 我出身青樓酱床,卻偏偏與公主長(zhǎng)得像羊赵,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子扇谣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評(píng)論 2 348