版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2018.10.14 星期日 |
前言
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)的簡單示例(一)
Annotations - 注釋
如果您曾在地圖應(yīng)用中搜索過某個(gè)位置掰读,那么您已經(jīng)看到了地圖上顯示的那些彩色pins
秘狞。 這些稱為annotations
,使用MKAnnotationView
創(chuàng)建蹈集。 您可以在自己的應(yīng)用程序中使用annotations
- 您可以使用您想要的任何圖像烁试,而不僅僅是pins
!
在您的應(yīng)用中拢肆,annotations
將非常有用廓潜,可以幫助指出公園游客的特定景點(diǎn)。 annotations
對(duì)象與MKOverlay
和MKOverlayRenderer
的工作方式類似善榛,但您將使用MKAnnotation
和MKAnnotationView
辩蛋。
在Annotationations
組中創(chuàng)建一個(gè)名為AttractionAnnotation.swift
的新Swift文件。 用以下內(nèi)容替換其內(nèi)容:
import UIKit
import MapKit
enum AttractionType: Int {
case misc = 0
case ride
case food
case firstAid
func image() -> UIImage {
switch self {
case .misc:
return #imageLiteral(resourceName: "star")
case .ride:
return #imageLiteral(resourceName: "ride")
case .food:
return #imageLiteral(resourceName: "food")
case .firstAid:
return #imageLiteral(resourceName: "firstaid")
}
}
}
class AttractionAnnotation: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
var type: AttractionType
init(coordinate: CLLocationCoordinate2D, title: String, subtitle: String, type: AttractionType) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
self.type = type
}
}
在這里移盆,您首先為AttractionType
定義一個(gè)枚舉悼院,以幫助您將每個(gè)吸引力分類為一個(gè)類型。 這個(gè)枚舉列出了四種類型的注釋:misc, rides, foods and first aid
咒循。 另外還有一個(gè)方便的函數(shù)來獲取正確的annotation
圖像据途。
接下來,您聲明此類符合MKAnnotation Protocol叙甸。 與MKOverlay
非常相似颖医,MKAnnotation
具有必需的coordinate
屬性。 您可以定義特定于此實(shí)現(xiàn)的少數(shù)屬性裆蒸。 最后熔萧,定義一個(gè)初始化程序,允許您為每個(gè)屬性賦值。
現(xiàn)在佛致,您需要?jiǎng)?chuàng)建一個(gè)特定的MKAnnotation
實(shí)例以用于annotations
贮缕。
在Annotations
組下創(chuàng)建另一個(gè)名為AttractionAnnotationView.swift
的Swift文件。 用以下內(nèi)容替換其內(nèi)容:
import UIKit
import MapKit
class AttractionAnnotationView: MKAnnotationView {
// Required for MKAnnotationView
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
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()
}
}
MKAnnotationView
需要init(coder :)
初始化程序俺榆。 如果沒有它的定義感昼,錯(cuò)誤將阻止您構(gòu)建和運(yùn)行應(yīng)用程序。 為了防止這種情況罐脊,只需定義它并調(diào)用其超類初始化程序定嗓。 在這里,您還可以根據(jù)annotation
的type
屬性覆蓋init(annotation:reuseIdentifier :)
萍桌,在annotation
的image
屬性上設(shè)置不同的圖像蜕乡。
現(xiàn)在創(chuàng)建了annotation
及其關(guān)聯(lián)視圖,您可以開始將它們添加到地圖視圖中梗夸!
要確定每個(gè)annotation
的位置,您將使用MagicMountainAttractions.plist
文件中的信息号醉,您可以在Park Information
組下找到該文件反症。 plist文件包含坐標(biāo)信息和有關(guān)公園景點(diǎn)的其他詳細(xì)信息。
返回ParkMapViewController.swift
并插入以下方法:
func addAttractionPins() {
guard let attractions = Park.plist("MagicMountainAttractions") as? [[String : String]] else { return }
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"] ?? ""
let annotation = AttractionAnnotation(coordinate: coordinate, title: title, subtitle: subtitle, type: type)
mapView.addAnnotation(annotation)
}
}
此方法讀取MagicMountainAttractions.plist
并枚舉字典數(shù)組畔派。 對(duì)于每個(gè)條目铅碍,它使用attraction
的信息創(chuàng)建AttractionAnnotation
的實(shí)例,然后將每個(gè)annotation
添加到地圖視圖中线椰。
現(xiàn)在胞谈,您需要更新loadSelectedOptions()
以適應(yīng)此新選項(xiàng),并在用戶選擇它時(shí)執(zhí)行新方法憨愉。
更新loadSelectedOptions()
中的switch
語句以包含以下內(nèi)容:
case .mapPins:
addAttractionPins()
這會(huì)在需要時(shí)調(diào)用新的addAttractionPins()
方法烦绳。 請(qǐng)注意,對(duì)removeOverlays
的調(diào)用也會(huì)隱藏pins
覆蓋配紫。
已經(jīng)完成的差不多了径密! 最后但并非最不重要的是,您需要實(shí)現(xiàn)另一個(gè)代理方法躺孝,該方法將MKAnnotationView
實(shí)例提供給地圖視圖享扔,以便它可以自己呈現(xiàn)它們。
將以下方法添加到文件底部的MKMapViewDelegate
類擴(kuò)展中:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = AttractionAnnotationView(annotation: annotation, reuseIdentifier: "Attraction")
annotationView.canShowCallout = true
return annotationView
}
此方法接收選定的MKAnnotation
并使用它來創(chuàng)建AttractionAnnotationView
植袍。 由于屬性canShowCallout
設(shè)置為true
惧眠,因此當(dāng)用戶觸摸annotation
時(shí)將顯示標(biāo)注。 最后于个,該方法返回annotation view
氛魁。
Build并運(yùn)行以查看您的annotation
!
打開Attraction Pins
以查看結(jié)果,如下面的屏幕截圖所示:
此時(shí)吸引針看起來相當(dāng)“尖銳”呆盖!
到目前為止拖云,您已經(jīng)介紹了很多復(fù)雜的MapKit,包括overlays and annotations
应又。 但是如果你需要使用一些繪圖基元宙项,比如線條,形狀和圓圈呢株扛?
MapKit
框架還使您能夠直接在地圖視圖上繪制尤筐。 MapKit
就是為此目的提供MKPolyline
,MKPolygon
和MKCircle
洞就。
I Walk The Line – MKPolyline - 我走線 - MKPolyline
如果你去過Magic Mountain
盆繁,你就會(huì)知道Goliath過山車是一次令人難以置信的騎行,有些騎手喜歡在走進(jìn)大門后直奔它旬蟋!
為了幫助這些騎手油昂,您將繪制從公園入口到Goliath
的路徑。
MKPolyline
是繪制連接多個(gè)點(diǎn)的路徑的絕佳解決方案倾贰,例如繪制從A點(diǎn)到B點(diǎn)的非線性路線冕碟。
要繪制折線,您需要按照代碼繪制它們的順序設(shè)置一系列經(jīng)度和緯度坐標(biāo)匆浙。
EntranceToGoliathRoute.plist
(再次在Park Information
文件夾中找到)包含路徑信息安寺。
您需要一種方法來讀取該plist
文件并為騎手創(chuàng)建路線。
打開ParkMapViewController.swift
并將以下方法添加到類中:
func addRoute() {
guard let points = Park.plist("EntranceToGoliathRoute") as? [String] else { return }
let cgPoints = points.map { CGPointFromString($0) }
let coords = cgPoints.map { CLLocationCoordinate2DMake(CLLocationDegrees($0.x), CLLocationDegrees($0.y)) }
let myPolyline = MKPolyline(coordinates: coords, count: coords.count)
mapView.add(myPolyline)
}
此方法讀取EntranceToGoliathRoute.plist
首尼,并將各個(gè)坐標(biāo)字符串轉(zhuǎn)換為CLLocationCoordinate2D
結(jié)構(gòu)挑庶。
在您的應(yīng)用中實(shí)現(xiàn)折線非常簡單;你只需創(chuàng)建一個(gè)包含所有點(diǎn)的數(shù)組软能,并將其傳遞給MKPolyline
迎捺! 不會(huì)比這更簡單了吧。
現(xiàn)在查排,您需要添加一個(gè)選項(xiàng)破加,以允許用戶打開或關(guān)閉折線路徑。
更新loadSelectedOptions()
以包含另一個(gè)case
語句:
case .mapRoute:
addRoute()
這在需要時(shí)調(diào)用addRoute()
方法雹嗦。
最后范舀,要將它們組合在一起,您需要更新代理方法了罪,以便它返回您要在地圖視圖上呈現(xiàn)的實(shí)際視圖锭环。
用這個(gè)替換mapView(_:rendererForOverlay)
:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is ParkMapOverlay {
return ParkMapOverlayView(overlay: overlay, overlayImage: #imageLiteral(resourceName: "overlay_park"))
} else if overlay is MKPolyline {
let lineView = MKPolylineRenderer(overlay: overlay)
lineView.strokeColor = UIColor.green
return lineView
}
return MKOverlayRenderer()
}
這里的更改是查找MKPolyline
對(duì)象的新增的else if
分支。 顯示折線視圖的過程與先前的疊加視圖非常相似泊藕。 但是辅辩,在這種情況下,您不需要?jiǎng)?chuàng)建任何自定義視圖對(duì)象。 您只需使用提供的MKPolyLineRenderer框架玫锋,并使用疊加層初始化新實(shí)例蛾茉。
MKPolyLineRenderer
還使您能夠更改折線的某些屬性。 在這種情況下撩鹿,您已修改筆畫的顏色以顯示為綠色谦炬。
Build并運(yùn)行您的應(yīng)用程序,啟用Route
選項(xiàng)节沦,它將顯示在屏幕上:
Goliath
狂熱分子現(xiàn)在可以在快速的時(shí)間內(nèi)登上過山車键思!
向公園顧客展示實(shí)際公園邊界是很好的,因?yàn)楣珗@實(shí)際上并沒有占據(jù)屏幕上顯示的整個(gè)空間甫贯。
雖然您可以使用MKPolyline
在公園邊界周圍繪制形狀吼鳞,但MapKit提供了另一個(gè)專門用于繪制閉合多邊形的類:MKPolygon
。
Don’t Fence Me In – MKPolygon - 不要圍困我 - MKPolygon
MKPolygon與MKPolyline
非常相似叫搁,只是坐標(biāo)集中的第一個(gè)和最后一個(gè)點(diǎn)相互連接以創(chuàng)建閉合形狀赔桌。
您將創(chuàng)建一個(gè)MKPolygon
作為顯示公園邊界的疊加層。 公園邊界坐標(biāo)已在MagicMountain.plist
中定義渴逻;返回并查看init(filename :)
以查看plist
文件中讀取邊界點(diǎn)的位置疾党。
將以下方法添加到ParkMapViewController.swift
:
func addBoundary() {
mapView.add(MKPolygon(coordinates: park.boundary, count: park.boundary.count))
}
上面的addBoundary()
的實(shí)現(xiàn)非常簡單。 給定park
實(shí)例的邊界數(shù)組和點(diǎn)數(shù)裸卫,您可以快速輕松地創(chuàng)建新的MKPolygon
實(shí)例!
你能猜到下一步嗎纽竣? 這與你上面為MKPolyline
所做的非常相似墓贿。
是的,這是正確的 - 在loadSelectedOptions
的switch
中插入另一個(gè)case
來處理顯示或隱藏公園邊界的新選項(xiàng):
case .mapBoundary:
addBoundary()
正如MKPolyline
一樣蜓氨,MKPolygon
符合MKOverlay
聋袋,因此您需要再次更新委托方法。
更新ParkMapViewController.swift
中的委托方法穴吹,如下所示:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is ParkMapOverlay {
return ParkMapOverlayView(overlay: overlay, overlayImage: #imageLiteral(resourceName: "overlay_park"))
} else if overlay is MKPolyline {
let lineView = MKPolylineRenderer(overlay: overlay)
lineView.strokeColor = UIColor.green
return lineView
} else if overlay is MKPolygon {
let polygonView = MKPolygonRenderer(overlay: overlay)
polygonView.strokeColor = UIColor.magenta
return polygonView
}
return MKOverlayRenderer()
}
代理方法的更新與以前一樣簡單幽勒。 您將MKOverlayView
創(chuàng)建為MKPolygonRenderer
的實(shí)例,并將筆觸顏色設(shè)置為洋紅色港令。
運(yùn)行應(yīng)用程序以查看您的新邊界:
這照顧了折線和多邊形啥容。 要講述的最后一種繪圖方法是繪制圓圈作為疊加層,由MKCircle
巧妙地處理顷霹。
Circle In The Sand – MKCircle - 在沙子圈 - MKCircle
MKCircle也非常類似于MKPolyline
和MKPolygon
咪惠,除了它繪制一個(gè)圓,給定一個(gè)坐標(biāo)點(diǎn)作為圓的中心淋淀,以及一個(gè)確定圓的大小的半徑遥昧。
標(biāo)記公園角色的一般位置會(huì)很棒。 在地圖上繪制一些圓圈以模擬這些角色的位置!
MKCircle
疊加層是實(shí)現(xiàn)此功能的一種非常簡單的方法炭臭。
Park Information
文件夾還包含字符位置文件永脓。 每個(gè)文件都是用戶角色的幾個(gè)坐標(biāo)的數(shù)組。
在名為Models
組下創(chuàng)建一個(gè)新的Swift文件Character.swift
鞋仍。 用以下代碼替換其內(nèi)容:
import UIKit
import MapKit
class Character: MKCircle {
var name: String?
var color: UIColor?
convenience init(filename: String, color: UIColor) {
guard let points = Park.plist(filename) as? [String] else { self.init(); return }
let cgPoints = points.map { CGPointFromString($0) }
let coords = cgPoints.map { CLLocationCoordinate2DMake(CLLocationDegrees($0.x), CLLocationDegrees($0.y)) }
let randomCenter = coords[Int(arc4random()%4)]
let randomRadius = CLLocationDistance(max(5, Int(arc4random()%40)))
self.init(center: randomCenter, radius: randomRadius)
self.name = filename
self.color = color
}
}
您剛添加的新類符合MKCircle
協(xié)議常摧,并定義了兩個(gè)可選屬性:name
和color
。 便捷初始化程序接受plist
文件名和顏色來繪制圓凿试。 然后排宰,它從plist文件中讀取數(shù)據(jù),并從文件中的四個(gè)位置中選擇一個(gè)隨機(jī)位置那婉。 接下來板甘,它選擇一個(gè)隨機(jī)半徑來模擬時(shí)間方差。 返回的MKCircle
已設(shè)置好并準(zhǔn)備好放在地圖上详炬!
現(xiàn)在您需要一種方法來添加每個(gè)角色盐类。 打開ParkMapViewController.swift
并將以下方法添加到類中:
func addCharacterLocation() {
mapView.add(Character(filename: "BatmanLocations", color: .blue))
mapView.add(Character(filename: "TazLocations", color: .orange))
mapView.add(Character(filename: "TweetyBirdLocations", color: .yellow))
}
上述方法幾乎對(duì)每個(gè)角色執(zhí)行相同的操作。 它會(huì)傳遞每個(gè)文件的plist
文件名呛谜,決定顏色并將其作為疊加層添加到地圖中在跳。
你幾乎完成! 你能回憶起最后幾步應(yīng)該是什么嗎隐岛?
是的猫妙,你仍然需要提供一個(gè)MKOverlayView
的地圖視圖,這是通過委托方法完成的聚凹。
更新ParkMapViewController.swift
中的委托方法:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is ParkMapOverlay {
return ParkMapOverlayView(overlay: overlay, overlayImage: #imageLiteral(resourceName: "overlay_park"))
} else if overlay is MKPolyline {
let lineView = MKPolylineRenderer(overlay: overlay)
lineView.strokeColor = UIColor.green
return lineView
} else if overlay is MKPolygon {
let polygonView = MKPolygonRenderer(overlay: overlay)
polygonView.strokeColor = UIColor.magenta
return polygonView
} else if let character = overlay as? Character {
let circleView = MKCircleRenderer(overlay: character)
circleView.strokeColor = character.color
return circleView
}
return MKOverlayRenderer()
}
最后割坠,更新loadSelectedOptions()
以向用戶提供打開或關(guān)閉角色位置的選項(xiàng):
case .mapCharacterLocation:
addCharacterLocation()
您也可以刪除default:
和break
語句,因?yàn)槟呀?jīng)涵蓋了所有可能的情況妒牙。
Build并運(yùn)行應(yīng)用程序彼哼,并打開角色覆蓋圖以查看每個(gè)人隱藏的位置!
有更先進(jìn)的 - 也許更有效 - 的方法來創(chuàng)建疊加層湘今。 一些替代方法是使用KML文件敢朱,MapBox tiles
或其他第三方提供的資源。
源碼
1. Swift
首先看一下工程文件
然后看一下sb中的內(nèi)容
下面就是詳細(xì)的代碼了摩瞎。
1. Character.swift
import UIKit
import MapKit
class Character: MKCircle {
var name: String?
var color: UIColor?
convenience init(filename: String, color: UIColor) {
guard let points = Park.plist(filename) as? [String] else { self.init(); return }
let cgPoints = points.map { CGPointFromString($0) }
let coords = cgPoints.map { CLLocationCoordinate2DMake(CLLocationDegrees($0.x), CLLocationDegrees($0.y)) }
let randomCenter = coords[Int(arc4random()%4)]
let randomRadius = CLLocationDistance(max(5, Int(arc4random()%40)))
self.init(center: randomCenter, radius: randomRadius)
self.name = filename
self.color = color
}
}
2. Park.swift
import UIKit
import MapKit
class Park {
var name: String?
var boundary: [CLLocationCoordinate2D] = []
var midCoordinate = CLLocationCoordinate2D()
var overlayTopLeftCoordinate = CLLocationCoordinate2D()
var overlayTopRightCoordinate = CLLocationCoordinate2D()
var overlayBottomLeftCoordinate = CLLocationCoordinate2D()
var overlayBottomRightCoordinate: CLLocationCoordinate2D {
get {
return CLLocationCoordinate2DMake(overlayBottomLeftCoordinate.latitude,
overlayTopRightCoordinate.longitude)
}
}
var overlayBoundingMapRect: MKMapRect {
get {
let topLeft = MKMapPointForCoordinate(overlayTopLeftCoordinate);
let topRight = MKMapPointForCoordinate(overlayTopRightCoordinate);
let bottomLeft = MKMapPointForCoordinate(overlayBottomLeftCoordinate);
return MKMapRectMake(
topLeft.x,
topLeft.y,
fabs(topLeft.x - topRight.x),
fabs(topLeft.y - bottomLeft.y))
}
}
init(filename: String) {
guard let properties = Park.plist(filename) as? [String : Any],
let boundaryPoints = properties["boundary"] as? [String] else { return }
midCoordinate = Park.parseCoord(dict: properties, fieldName: "midCoord")
overlayTopLeftCoordinate = Park.parseCoord(dict: properties, fieldName: "overlayTopLeftCoord")
overlayTopRightCoordinate = Park.parseCoord(dict: properties, fieldName: "overlayTopRightCoord")
overlayBottomLeftCoordinate = Park.parseCoord(dict: properties, fieldName: "overlayBottomLeftCoord")
let cgPoints = boundaryPoints.map { CGPointFromString($0) }
boundary = cgPoints.map { CLLocationCoordinate2DMake(CLLocationDegrees($0.x), CLLocationDegrees($0.y)) }
}
static func plist(_ plist: String) -> Any? {
guard let filePath = Bundle.main.path(forResource: plist, ofType: "plist"),
let data = FileManager.default.contents(atPath: filePath) else { return nil }
do {
return try PropertyListSerialization.propertyList(from: data, options: [], format: nil)
} catch {
return nil
}
}
static func parseCoord(dict: [String: Any], fieldName: String) -> CLLocationCoordinate2D{
if let coord = dict[fieldName] as? String {
let point = CGPointFromString(coord)
return CLLocationCoordinate2DMake(CLLocationDegrees(point.x), CLLocationDegrees(point.y))
}
return CLLocationCoordinate2D()
}
}
3. ParkMapOverlay.swift
import UIKit
import MapKit
class ParkMapOverlay: NSObject, MKOverlay {
var coordinate: CLLocationCoordinate2D
var boundingMapRect: MKMapRect
init(park: Park) {
boundingMapRect = park.overlayBoundingMapRect
coordinate = park.midCoordinate
}
}
4. ParkMapOverlayView.swift
import UIKit
import MapKit
class ParkMapOverlayView: MKOverlayRenderer {
var overlayImage: UIImage
init(overlay:MKOverlay, overlayImage:UIImage) {
self.overlayImage = overlayImage
super.init(overlay: overlay)
}
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)
}
}
5. AttractionAnnotation.swift
import UIKit
import MapKit
enum AttractionType: Int {
case misc = 0
case ride
case food
case firstAid
func image() -> UIImage {
switch self {
case .misc:
return #imageLiteral(resourceName: "star")
case .ride:
return #imageLiteral(resourceName: "ride")
case .food:
return #imageLiteral(resourceName: "food")
case .firstAid:
return #imageLiteral(resourceName: "firstaid")
}
}
}
class AttractionAnnotation: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
var type: AttractionType
init(coordinate: CLLocationCoordinate2D, title: String, subtitle: String, type: AttractionType) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
self.type = type
}
}
6. AttractionAnnotationView.swift
import UIKit
import MapKit
class AttractionAnnotationView: MKAnnotationView {
// Required for MKAnnotationView
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
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()
}
}
7. MapOptionsViewController.swift
import UIKit
enum MapOptionsType: Int {
case mapBoundary = 0
case mapOverlay
case mapPins
case mapCharacterLocation
case mapRoute
func displayName() -> String {
switch (self) {
case .mapBoundary:
return "Park Boundary"
case .mapOverlay:
return "Map Overlay"
case .mapPins:
return "Attraction Pins"
case .mapCharacterLocation:
return "Character Location"
case .mapRoute:
return "Route"
}
}
}
class MapOptionsViewController: UIViewController {
var selectedOptions = [MapOptionsType]()
}
// MARK: - UITableViewDataSource
extension MapOptionsViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "OptionCell")!
if let mapOptionsType = MapOptionsType(rawValue: indexPath.row) {
cell.textLabel!.text = mapOptionsType.displayName()
cell.accessoryType = selectedOptions.contains(mapOptionsType) ? .checkmark : .none
}
return cell
}
}
// MARK: - UITableViewDelegate
extension MapOptionsViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let cell = tableView.cellForRow(at: indexPath) else { return }
guard let mapOptionsType = MapOptionsType(rawValue: indexPath.row) else { return }
if (cell.accessoryType == .checkmark) {
// Remove option
selectedOptions = selectedOptions.filter { $0 != mapOptionsType}
cell.accessoryType = .none
} else {
// Add option
selectedOptions += [mapOptionsType]
cell.accessoryType = .checkmark
}
tableView.deselectRow(at: indexPath, animated: true)
}
}
8. ParkMapViewController.swift
import UIKit
import MapKit
class ParkMapViewController: UIViewController {
@IBOutlet weak var mapView: MKMapView!
var park = Park(filename: "MagicMountain")
var selectedOptions : [MapOptionsType] = []
override func viewDidLoad() {
super.viewDidLoad()
let latDelta = park.overlayTopLeftCoordinate.latitude - park.overlayBottomRightCoordinate.latitude
// Think of a span as a tv size, measure from one corner to another
let span = MKCoordinateSpanMake(fabs(latDelta), 0.0)
let region = MKCoordinateRegionMake(park.midCoordinate, span)
mapView.region = region
}
// MARK: - Add methods
func addOverlay() {
let overlay = ParkMapOverlay(park: park)
mapView.add(overlay)
}
func addAttractionPins() {
guard let attractions = Park.plist("MagicMountainAttractions") as? [[String : String]] else { return }
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"] ?? ""
let annotation = AttractionAnnotation(coordinate: coordinate, title: title, subtitle: subtitle, type: type)
mapView.addAnnotation(annotation)
}
}
func addRoute() {
guard let points = Park.plist("EntranceToGoliathRoute") as? [String] else { return }
let cgPoints = points.map { CGPointFromString($0) }
let coords = cgPoints.map { CLLocationCoordinate2DMake(CLLocationDegrees($0.x), CLLocationDegrees($0.y)) }
let myPolyline = MKPolyline(coordinates: coords, count: coords.count)
mapView.add(myPolyline)
}
func addBoundary() {
mapView.add(MKPolygon(coordinates: park.boundary, count: park.boundary.count))
}
func addCharacterLocation() {
mapView.add(Character(filename: "BatmanLocations", color: .blue))
mapView.add(Character(filename: "TazLocations", color: .orange))
mapView.add(Character(filename: "TweetyBirdLocations", color: .yellow))
}
// MARK: Helper methods
func loadSelectedOptions() {
mapView.removeAnnotations(mapView.annotations)
mapView.removeOverlays(mapView.overlays)
for option in selectedOptions {
switch (option) {
case .mapOverlay:
self.addOverlay()
case .mapPins:
self.addAttractionPins()
case .mapRoute:
self.addRoute()
case .mapBoundary:
self.addBoundary()
case .mapCharacterLocation:
self.addCharacterLocation()
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
(segue.destination as? MapOptionsViewController)?.selectedOptions = selectedOptions
}
@IBAction func closeOptions(_ exitSegue: UIStoryboardSegue) {
guard let vc = exitSegue.source as? MapOptionsViewController else { return }
selectedOptions = vc.selectedOptions
loadSelectedOptions()
}
@IBAction func mapTypeChanged(_ sender: UISegmentedControl) {
mapView.mapType = MKMapType.init(rawValue: UInt(sender.selectedSegmentIndex)) ?? .standard
}
}
// MARK: - MKMapViewDelegate
extension ParkMapViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is ParkMapOverlay {
return ParkMapOverlayView(overlay: overlay, overlayImage: #imageLiteral(resourceName: "overlay_park"))
} else if overlay is MKPolyline {
let lineView = MKPolylineRenderer(overlay: overlay)
lineView.strokeColor = UIColor.green
return lineView
} else if overlay is MKPolygon {
let polygonView = MKPolygonRenderer(overlay: overlay)
polygonView.strokeColor = UIColor.magenta
return polygonView
} else if let character = overlay as? Character {
let circleView = MKCircleRenderer(overlay: character)
circleView.strokeColor = character.color
return circleView
}
return MKOverlayRenderer()
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = AttractionAnnotationView(annotation: annotation, reuseIdentifier: "Attraction")
annotationView.canShowCallout = true
return annotationView
}
}
下面是最終的所有選項(xiàng)都勾選的效果圖
下面是gif圖
后記
本篇主要講述了一個(gè)疊加視圖相關(guān)的簡單示例拴签,感興趣給個(gè)贊或者關(guān)注~~~