In Chapter 1, your first iOS map application displayed one point on the map, with the default red pin at its location. The MapKit framework is capable of much more when it comes to displaying annotations. We can customize the images for the annotations and the callouts seen when a user taps on an annotation.
Let’s continue building on top of the FirstMapsApp project that we created in Chapter 1 and then added user locations in Chapter 2.
Understanding MapKit and annotations
The MapKit framework maintains a distinction between annotations – the data displayed on the map – and annotation views, the user interface elements that appear on top of the map at a given coordinate for an annotation. While MapKit provides the MKPointAnnotation class for basic point display, most map applications will use a custom implementation of the MKAnnotation protocol .
Similarly, MapKit also includes the MKPinAnnotationView and MKMarkerAnnotationView classes for displaying pins or markers on the app. The MKAnnotationView class is useful for displaying your own custom images. You can subclass the MKAnnotationView class or use it directly.
The mapView(_:viewFor:) function on the MKMapViewDelegate protocol allows your application to map MKAnnotationView objects to MKAnnotation objects. Because the MKPointAnnotation class has a limited number of data fields, using your own implementation of MKAnnotation gives you the most flexibility.
Using a custom annotation class
Implementing your own annotation class is straightforward – you will need to extend the NSObject class and then implement the MKAnnotation protocol .
An implementation of an annotation class in Swift
We will be using this MapPoint class and then later extending it through the course of this chapter. It’s likely that you would add additional fields to this annotation class for your specific application. Go ahead and create a new file in your Xcode project named MapPoint.swift, and add the code in Listing 3-1.
Display custom annotations
As the MapPoint class implements the MKAnnotation protocol , your code simply needs to construct new MapPoint objects and then add them to the map view as annotations.
Displaying custom annotations on a map view
After adding this code and running your project, you will not see anything too different from our earlier chapters. We have not customized the view for the annotations yet.
Customizing pins for annotations
The easiest way to change the view displayed for each annotation is to use the MKPinAnnotationView class. Your code can set the pin color in the mapView(_:viewFor:) function . This function comes from the MKMapViewDelegate, so you will need to set the delegate on the map view to either a custom class or to the current view controller. For the purposes of this chapter, we will create an extension to our view controller class that implements the MKMapViewDelegate and then set the delegate on the map view in code, in the viewDidLoad() method .
Using a pin annotation view
Now that we’ve demonstrated basic view annotation features, let’s move on to some more advanced functionality. The first thing to change will be that the user’s location is now showing up as a pin.
Handling the user location
This check would typically be done at the beginning of the mapView(_:viewFor:) function. When you return nil, the map will display the default annotation view.
Add the MKUserLocation check to the beginning of the mapView(_:viewFor:) method and then run the project again – the user location (if you have location enabled) will be the glowing blue dot, not a pin.
Reusing annotation views
While this code works, it could be improved. Similar to table views and reusable table view cells, map views support reusable annotations.
When working with annotation views, it’s important to use a reuse identifier. This allows the map view to recycle view objects as the user scrolls through the map. Some annotations will fall out of the visible rectangle of the map, and those views can be recycled for annotations that are now visible. Reusing these annotation views will allow for smooth scrolling, especially with large sets of annotations.
The MKMapView class has a dequeueReusableAnnotationView() function that takes a reuse identifier as an argument. If an annotation view is waiting in the reuse queue, you will get that instance. Any customizations you need to make to that annotation view (for instance, setting the annotation to the current annotation) should be done inside an if let block that checks if an instance was available.
If an instance wasn’t available in the reuse queue, you will need to create an annotation view and then properly configure it.
Reusing annotation views
While this code is more complicated than the previous code listing, scrolling the map will be much smoother if you have many annotations displayed on the map.
Dequeuing and creating annotation views
Simplified annotation reuse and creation after registering annotation view with map view
This has cleaned up our code considerably and would allow us to easily add additional annotation view types if we had multiple types of markers on our map.
Setting images on annotation views
Some of your map application ideas may work fine with the built-in pin or marker annotation view classes. However, it’s likely that you will need to use the MKAnnotationView class so that you can set your own images.
We will change our code slightly to create annotation views for a dog park. The reuse identifier will be “DogPark”, and we will also have an image of a dog in a park, stored in our iOS app’s assets. This image will also be named “DogPark”. You will need to download your own image and place it into your Xcode project’s assets, so that you have something to display.
Use an image on the annotation view
After you replace the code that dequeued the reusable pin annotation views with Listing 3-6, run the project. You will see the dog park image appear in place of your pins.
Using callouts with annotations
As you can see, the way you display annotations on your map is extremely customizable. Try changing up the images for each annotation based on a data point, or try displaying some as images and some as pins.
Summary
In this chapter, we’ve built on the mapping fundamentals from Chapter 1 to customize the markers we display on the map. We’ve seen how to use reusable annotation views to improve performance for maps with many markers.
In the next chapter, we will discuss how to use Apple’s local search functionality to provide points of interest from their database for your map.