Annotation models

This example requires multiple files:

  • CustomAnnotationModels defines two annotation classes:

    1. CustomPointAnnotation: Implements the MGLAnnotation protocol and adds custom image and reuseIdentifier properties.
    2. CustomPolyline: Subclasses MGLPolyline and adds a custom color property.
  • CustomAnnotationModelViewController uses these custom classes with MGLMapView.

For an example of subclassing MGLAnnotationView, see the annotation views example.

To learn about more ways to add points to a map, check out the Markers and annotations guide.

import Mapbox
// MGLAnnotation protocol reimplementation
class CustomPointAnnotation: NSObject, MGLAnnotation {
// As a reimplementation of the MGLAnnotation protocol, we have to add mutable coordinate and (sub)title properties ourselves.
var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
// Custom properties that we will use to customize the annotation's image.
var image: UIImage?
var reuseIdentifier: String?
init(coordinate: CLLocationCoordinate2D, title: String?, subtitle: String?) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
// MGLPolyline subclass
class CustomPolyline: MGLPolyline {
// Because this is a subclass of MGLPolyline, there is no need to redeclare its properties.
// Custom property that we will use when drawing the polyline.
var color: UIColor?
import Mapbox
class ViewController: UIViewController, MGLMapViewDelegate {
override func viewDidLoad() {
let mapView = MGLMapView(frame: view.bounds)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.styleURL = MGLStyle.lightStyleURL
mapView.tintColor = .darkGray
mapView.zoomLevel = 1
mapView.delegate = self
// Polyline
// Create a coordinates array with all of the coordinates for our polyline.
var coordinates = [
CLLocationCoordinate2D(latitude: 35, longitude: -25),
CLLocationCoordinate2D(latitude: 20, longitude: -30),
CLLocationCoordinate2D(latitude: 0, longitude: -25),
CLLocationCoordinate2D(latitude: -15, longitude: 0),
CLLocationCoordinate2D(latitude: -45, longitude: 10),
CLLocationCoordinate2D(latitude: -45, longitude: 40),
let polyline = CustomPolyline(coordinates: &coordinates, count: UInt(coordinates.count))
// Set the custom `color` property, later used in the `mapView:strokeColorForShapeAnnotation:` delegate method.
polyline.color = .darkGray
// Add the polyline to the map. Note that this method name is singular.
// Point Annotations
// Add a custom point annotation for every coordinate (vertex) in the polyline.
var pointAnnotations = [CustomPointAnnotation]()
for coordinate in coordinates {
let count = pointAnnotations.count + 1
let point = CustomPointAnnotation(coordinate: coordinate,
title: "Custom Point Annotation \(count)",
subtitle: nil)
// Set the custom `image` and `reuseIdentifier` properties, later used in the `mapView:imageForAnnotation:` delegate method.
// Create a unique reuse identifier for each new annotation image.
point.reuseIdentifier = "customAnnotation\(count)"
// This dot image grows in size as more annotations are added to the array.
point.image = dot(size:5 * count)
// Append each annotation to the array, which will be added to the map all at once.
// Add the point annotations to the map. This time the method name is plural.
// If you have multiple annotations to add, batching their addition to the map is more efficient.
func dot(size: Int) -> UIImage {
let floatSize = CGFloat(size)
let rect = CGRect(x: 0, y: 0, width: floatSize, height: floatSize)
let strokeWidth: CGFloat = 1
UIGraphicsBeginImageContextWithOptions(rect.size, false, UIScreen.main.scale)
let ovalPath = UIBezierPath(ovalIn: rect.insetBy(dx: strokeWidth, dy: strokeWidth))
ovalPath.lineWidth = strokeWidth
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
return image
// MARK: - MGLMapViewDelegate methods
func mapView(_ mapView: MGLMapView, imageFor annotation: MGLAnnotation) -> MGLAnnotationImage? {
if let point = annotation as? CustomPointAnnotation,
let image = point.image,
let reuseIdentifier = point.reuseIdentifier {
if let annotationImage = mapView.dequeueReusableAnnotationImage(withIdentifier: reuseIdentifier) {
// The annotatation image has already been cached, just reuse it.
return annotationImage
} else {
// Create a new annotation image.
return MGLAnnotationImage(image: image, reuseIdentifier: reuseIdentifier)
// Fallback to the default marker image.
return nil
func mapView(_ mapView: MGLMapView, strokeColorForShapeAnnotation annotation: MGLShape) -> UIColor {
if let annotation = annotation as? CustomPolyline {
// Return orange if the polyline does not have a custom color.
return annotation.color ?? .orange
// Fallback to the default tint color.
return mapView.tintColor
func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
return true