NEW: Master Swift design patterns with my latest book! >>

< Previous: Setting up   Next: Annotations and accessory views: MKPinAnnotationView >

Up and running with MapKit

Select your project in the project navigator – it's the top thing, with a blue icon next to it. In the center of the Xcode window you'll see lots of options, and this is usually where you limit the orientation of your app. But this time, I want you to go up to the list of tabs and change from General to Capabilities, because we're going to ask that our app be allowed to use maps.

There are lots of capabilities you can request on this screen, but the one we're looking for right now is called simply "Maps". Find it, then change its switch from Off to On. You should be able to try running your app again, because this time will work.

In the picture below you can see the Entitlements tab showing map support being enabled.

You must use the Entitlements tab to enable support for MapKit.

You'll see a default map view, and you can pan around, zoom in and out, and so on. If you were wondering, you need to hold down Alt to trigger a virtual "pinch" gesture – just click and drag as if you were moving one finger, and the other "finger" will move in the opposite direction.

Using the assistant editor, please create an outlet for your map view called mapView. You should also set your view controller to be the delegate of the map view by Ctrl-dragging from the map view to the orange and white view controller button just above the layout area. You will also need to add import MapKit to ViewController.swift so it understands what MKMapView is.

Note: If you don’t set the map’s delegate, the rest of this project won’t work too well.

With that done, we're going to add some annotations to our map. Annotations are objects that contain a title, a subtitle and a position. The first two are both strings, the third is a new data type called CLLocationCoordinate2D, which is a structure that holds a latitude and longitude for where the annotation should be placed.

Map annotations are described not as a class, but as a protocol. This is something you haven't seen before, because so far protocols have all been about methods. But if we want to conform to the MKAnnotation protocol, which is the one we need to adopt in order to create map annotations, it states that we must have a coordinate in our annotation. That makes sense, because there's no point in having an annotation on a map if we don't know where it is. The title and subtitle are optional, but we'll provide them anyway.

Create a new file and choose iOS > Source > Cocoa Touch Class. Make it a subclass of NSObject and name it “Capital”. With map annotations, you can't use structs, and you must inherit from NSObject because it needs to be interactive with Apple's Objective-C code.

Change the contents of Capital.swift to this:

import MapKit
import UIKit

class Capital: NSObject, MKAnnotation {
    var title: String?
    var coordinate: CLLocationCoordinate2D
    var info: String

    init(title: String, coordinate: CLLocationCoordinate2D, info: String) {
        self.title = title
        self.coordinate = coordinate
        self.info = info
    }
}

There are our three properties, along with a basic initializer that just copies in the data it's given. Again, we need to use self. here because the parameters being passed in are the same name as our properties. I've added import MapKit to the file because that's where MKAnnotation and CLLocationCoordinate2D are defined.

With this custom subclass, we can create capital cities by passing in their name, coordinate and information – I'll be using the info property to hold one priceless (read: off-the-cuff, I sucked at geography) informational nugget about each city. You're welcome to do better!

Put these lines into the viewDidLoad() method of ViewController.swift:

let london = Capital(title: "London", coordinate: CLLocationCoordinate2D(latitude: 51.507222, longitude: -0.1275), info: "Home to the 2012 Summer Olympics.")
let oslo = Capital(title: "Oslo", coordinate: CLLocationCoordinate2D(latitude: 59.95, longitude: 10.75), info: "Founded over a thousand years ago.")
let paris = Capital(title: "Paris", coordinate: CLLocationCoordinate2D(latitude: 48.8567, longitude: 2.3508), info: "Often called the City of Light.")
let rome = Capital(title: "Rome", coordinate: CLLocationCoordinate2D(latitude: 41.9, longitude: 12.5), info: "Has a whole country inside it.")
let washington = Capital(title: "Washington DC", coordinate: CLLocationCoordinate2D(latitude: 38.895111, longitude: -77.036667), info: "Named after George himself.")

These Capital objects conform to the MKAnnotation protocol, which means we can send it to map view for display using the addAnnotation() method. Put this just before the end of viewDidLoad():

mapView.addAnnotation(london)
mapView.addAnnotation(oslo)
mapView.addAnnotation(paris)
mapView.addAnnotation(rome)
mapView.addAnnotation(washington)

Alternatively, you can add multiple annotations at once using the addAnnotations() method. Using this, you would replace those five lines with this:

mapView.addAnnotations([london, oslo, paris, rome, washington])

That creates an array out of the annotations and sends it in one lump to the map view.

If you run your program now, you'll see pins on the map for each city, and you can tap any of them to see the city name. But where's the info property? To show more information, we need to customize the view used to show the annotations.

Our pins are visible on the map, but they don't do anything more than just show each city's name.

Master iOS 11 now!

My book Practical iOS 11 gives you seven complete coding projects that teach all the major new features in iOS 11 in a smart, practical way.

< Previous: Setting up   Next: Annotations and accessory views: MKPinAnnotationView >
Click here to visit the Hacking with Swift store >>