MapKit Tutorial: Getting Started
Learn to use the powerful MapKit framework to build an interactive map, displaying location details and launching Maps for driving directions. By Andrew Tetlaw.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
MapKit Tutorial: Getting Started
35 mins
- Getting Started
- Setting the Visible Area
- Constraining the Camera
- Obtaining Public Art Data
- GeoJSON Properties
- Showing Artwork on the Map
- The Artwork Class
- Adding an Annotation
- Configuring the Annotation View
- The Map View Delegate
- Launching the Maps App
- Handling the Callout
- Setting Your Simulated Location
- Decoding GeoJSON with MKGeoJSONDecoder
- Making Annotations
- Plotting the Artwork
- Customizing Annotations
- Markers with Color-Coding and Text
- Color My World
- Annotations with Images
- Custom Callout Accessory Views
- Where To Go From Here?
GeoJSON Properties
Try not to get too worried about the GeoJSON data format. As you’ll soon see, importing it is mostly automatic! The essential parts are the properties dictionary and the coordinates.
For this tutorial, you’ll only use a few properties: The artwork’s location name, discipline, title, latitude and longitude:
- Location name: Lester McCoy Pavilion.
- Discipline: Mural.
- Title: The Makahiki Festival – The Makai Mural.
- Latitude: 21.290824.
- Longitude: -157.85131.
It’s a widely supported format for all things geospatial. Some useful tools include geojson.io, a useful site for testing and editing GeoJSON data, and GitHub, which will automatically render any .geojson file in a repository on a map.
It’s a widely supported format for all things geospatial. Some useful tools include geojson.io, a useful site for testing and editing GeoJSON data, and GitHub, which will automatically render any .geojson file in a repository on a map.
Later in this tutorial, you’ll use the whole dataset. But first, to jump straight into the MapKit fun, you’ll plot one of the artworks on the map.
Showing Artwork on the Map
In PublicArt.geojson, press Command-L and jump to line 1354. It’s a bronze statue of King David Kalakaua in Waikiki Gateway Park.
The properties for this item are:
- Location name: Waikiki Gateway Park
- Discipline: Sculpture
- Title: King David Kalakaua
- Latitude: 21.283921
- Longitude: -157.831661
To show this on the map view, you must create a map annotation. Map annotations are small pieces of information tied to a particular location. Apple’s Maps app usually represents them as little pins.
To create your annotations, you create a class that conforms to MKAnnotation
, add the annotation to the map and tell the map how the annotation should be displayed.
The Artwork Class
First, right-click the HonoluluArt folder in the Project navigator and pick New File…. Choose Swift File and name your new file Artwork.swift.
Open Artwork.swift in the editor and add the following below 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
}
}
To conform to MKAnnotation
, Artwork
must subclass NSObject
, because MKAnnotation
is an NSObjectProtocol
.
MKAnnotation
requires the coordinate
property. If you want your annotation view to display a title and subtitle when the user taps a marker, your class also needs properties named title
and subtitle
.
It’s perfectly sensible for the Artwork
class to have stored properties named title
and coordinate
, but none of the PublicArt.geojson properties naturally map to the idea of subtitle. To conform to MKAnnotation
, you make subtitle
a computed property that returns locationName
.
The MKAnnotation
protocol properties title
and subtitle
are defined as optional strings, but you may wonder why you’ve used String?
as the type for the locationName
and discipline
properties. Since these properties are set from an external data source, the PublicArt.geojson file, you can’t guarantee they’ll always exist. Better to be safe.
OK, so you’ll use the title
, locationName
and coordinate
properties for the MKAnnotation
object, but what’s the discipline
property for? You’ll find out later in this tutorial! ;]
Adding an Annotation
Next, you’ll add an Artwork
object to the map view for every artwork you want to plot. For now, you’re adding only one artwork, so switch to ViewController.swift and add the following lines to the end of 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)
Here, you create a new Artwork
object and add it as an annotation to the map view. MKMapView
also provides addAnnotations(_:)
, which you’ll use later in this tutorial when you have an array of annotations to add to the map view.
Build and run. Now you can see that King David Kalakaua’s statue is at the gateway to Waikiki!
The default annotation marker view shows the location with the title below the marker. Select the marker. It grows and now shows the subtitle, as well:
Well, that’s OK, but you’ve seen that tapping a marker in other apps shows a callout: a little, square speech bubble. For that, you must configure the annotation view.
Configuring the Annotation View
One way to configure the annotation view is to implement the map view’s mapView(_:viewFor:)
delegate method. Your job in this delegate method is to return an instance of MKAnnotationView
to present as a visual indicator of the annotation.
In this case, ViewController
is the delegate for the map view. To avoid clutter and improve readability, you’ll create an extension of ViewController
.
Add the following at the bottom of 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
}
}
Here’s what you’re doing:
If you have multiple styles of annotations, be sure to have a unique identifier for each one. Again, it’s the same idea behind a cell identifier in tableView(_:cellForRowAt:)
.
-
mapView(_:viewFor:)
gets called for every annotation you add to the map — liketableView(_:cellForRowAt:)
when working with table views — to return the view for each annotation. - Your app might use other annotations, like user location, so check that this annotation is an
Artwork
object. If it isn’t, returnnil
to let the map view use its default annotation view. - You create each view as an
MKMarkerAnnotationView
. Later in this tutorial, you’ll createMKAnnotationView
objects to display images instead of markers. - Also similarly to
tableView(_:cellForRowAt:)
, a map view reuses annotation views that are no longer visible. So you check to see if a reusable annotation view is available before creating a new one. When you dequeue a reusable annotation, you give it an identifier.If you have multiple styles of annotations, be sure to have a unique identifier for each one. Again, it’s the same idea behind a cell identifier in
tableView(_:cellForRowAt:)
. - Here you create a new
MKMarkerAnnotationView
object if an annotation view could not be dequeued. It uses the title and subtitle properties of yourArtwork
class to determine what to show in the callout.