MapKit Tutorial: Overlay Views
In this MapKit Overlay tutorial, you’ll learn how to draw images and lines over a native iOS map to make it more interactive for your users. By Rony Rozen.
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
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: Overlay Views
25 mins
- Getting Started
- All About Overlay Views
- Adding Your Information to the Map
- Creating Your First Map Overlay
- Adding Annotations
- Writing Your First Annotation
- Associating a View With Your Annotation
- Adding Annotations to the Map
- I Walk The Line: MKPolyline
- Don’t Fence Me In: MKPolygon
- Circle in the Sand: MKCircle
- Where to Go From Here?
While MapKit makes it easy to add a map to your app, that alone isn’t very engaging. Fortunately, you can use custom overlay views to make more appealing maps.
In this MapKit tutorial, you’ll create an app that showcases Six Flags Magic Mountain. By the time you’re done, you’ll have an interactive park map that shows attraction locations, ride routes and character locations. This app is for all you fast-ride thrill seekers out there. ;]
Getting Started
Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial. Then, open the project in Xcode.
The starter project includes the map you’ll work with and buttons to toggle the different types of overlays on and off.
Build and run. You’ll see something like this:
Once you feel ready, dive right into overlay views.
All About Overlay Views
Before you start creating overlay views, you need to understand two key classes: MKOverlay
and MKOverlayRenderer
.
MKOverlay
tells MapKit where you want it to draw the overlays. There are three steps for using this class:
- First, create your custom class that implements the
MKOverlay
protocol, which has two required properties:coordinate
andboundingMapRect
. These properties define where the overlay resides on the map and its size. - Then, create an instance of your class for each area where you want to display an overlay. In this app, for example, you’ll create an instance for a roller coaster overlay and another for a restaurant overlay.
- Finally, add the overlays to your map view.
At this point, the map knows where it’s supposed to display the overlays. But it doesn’t know what to display in each region.
This is where MKOverlayRenderer
comes in. Subclassing it lets you set up what you want to display in each spot.
For example, in this app, you’ll draw an image of the roller coaster or restaurant. MapKit expects to present a MKMapView
object, and this class defines the drawing infrastructure used by the map view.
Look at the starter project. In ContentView.swift, you’ll see a delegate method that lets you return an overlay view:
func mapView(
_ mapView: MKMapView,
rendererFor overlay: MKOverlay
) -> MKOverlayRenderer
MapKit calls this method when it realizes there’s an MKOverlay
object in the region the map view is displaying.
To sum up, you don’t add MKOverlayRenderer
objects directly to the map view. Instead, you tell the map about MKOverlay
objects to display and return MKOverlayRenderer
s when the delegate method requests them.
Now that you’ve covered the theory, it’s time to put these concepts to use!
Adding Your Information to the Map
Currently, the map doesn’t provide enough information about the park. Your task is to create an object that represents an overlay for the entire park.
First, select the Overlays group and create a new Swift file named ParkMapOverlay.swift. Then replace its contents with:
import MapKit
class ParkMapOverlay: NSObject, MKOverlay {
let coordinate: CLLocationCoordinate2D
let boundingMapRect: MKMapRect
init(park: Park) {
boundingMapRect = park.overlayBoundingMapRect
coordinate = park.midCoordinate
}
}
Conforming to MKOverlay
forces you to inherit from NSObject
. The initializer takes the properties from the passed Park
object, which is already in the starter project, and sets them to the corresponding MKOverlay
properties.
Next, you need to create a MKOverlayRenderer
that knows how to draw this overlay.
Create a new Swift file in the Overlays group called ParkMapOverlayView.swift. Replace its contents with:
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)
}
}
Here’s a breakdown of what you added:
-
init(overlay:overlayImage:)
overrides the base methodinit(overlay:)
by providing a second argument. -
draw(_:zoomScale:in:)
is the real meat of this class. It defines how MapKit should render this view when given a specificMKMapRect
,MKZoomScale
and theCGContext
of the graphic context, with the intent to draw the overlay image onto the context at the appropriate scale.
MKMapRect
to get a CGRect
in which to draw the image in the provided context. To learn more about Core Graphics, check out the Core Graphics tutorial series.
Great! Now that you have both an MKOverlay
and MKOverlayRenderer
, add them to your map view.
Creating Your First Map Overlay
In ContentView.swift, find addOverlay()
and change its TODO content to:
let overlay = ParkMapOverlay(park: park)
mapView.addOverlay(overlay)
This method adds an ParkMapOverlay
to the map view.
Take a look at updateMapOverlayViews()
. You’ll see when a user taps the button in the navigation bar to show the map overlay, addOverlay()
is called. Now that you’ve added the necessary code, the overlay displays.
Notice that updateMapOverlayViews()
also removes any annotations and overlays that may be present so you don’t end up with duplicate renderings. This is not necessarily efficient, but it’s a simple approach to clear previous items from the map.
The last step standing between you and seeing your newly implemented overlay on the map is mapView(_:rendererFor:)
, mentioned earlier. Replace its current TODO implementation with:
if overlay is ParkMapOverlay {
return ParkMapOverlayView(
overlay: overlay,
overlayImage: UIImage(imageLiteralResourceName: "overlay_park"))
}
When MapKit determines an MKOverlay
is in view, it calls this delegate method to obtain a renderer.
Here, you check if the overlay is of class type ParkMapOverlay
. If so, you load the overlay image, create a ParkMapOverlayView
instance with the overlay image and return this instance to the caller.
There’s one little piece missing, though: Where does that suspicious little overlay_park
image come from? It’s a PNG to overlay the map with the defined park’s boundary. The overlay_park
image, found in Assets.xcassets, looks like this:
Build and run, enable the :Overlay: option at the top of the screen and voilà! Here’s the park overlay drawn on top of your map:
Zoom in, zoom out, and move around. The overlay scales and moves as you would expect. Cool!