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.

4.8 (34) · 4 Reviews

Download materials
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

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.

Note: If you want to learn more about the format, there’s a whole world to explore. The official website is a bit sparse, the RFC is highly technical, but the Wikipedia article is readable. :]

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.

Photo of King David Kalakaua statue, by Wally Gobetz

Stately bronze statue of King David Kalakaua.

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!

Map of downtown Waikiki with pin showing statue's location.

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:

Zoomed in view of map with pin, title of statue and location in Waikiki Gateway Park.

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:).

  1. mapView(_:viewFor:) gets called for every annotation you add to the map — like tableView(_:cellForRowAt:) when working with table views — to return the view for each annotation.
  2. Your app might use other annotations, like user location, so check that this annotation is an Artwork object. If it isn’t, return nil to let the map view use its default annotation view.
  3. You create each view as an MKMarkerAnnotationView. Later in this tutorial, you’ll create MKAnnotationView objects to display images instead of markers.
  4. 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.
  5. Here you create a new MKMarkerAnnotationView object if an annotation view could not be dequeued. It uses the title and subtitle properties of your Artwork class to determine what to show in the callout.