Beta
Maps SDK for iOS v10
All docsMaps SDK for iOS v10GuidesMarkers and annotations

Markers and annotations

The Mapbox Maps SDK for iOS offers several ways to add markers and other shapes to a map. This guide helps you choose the best approach for your application based on factors like interaction requirements, number of features, the need for customizing the style of features, and data sources.

Annotations

You can add annotations to the map using point, line, polygon and circle shapes with the MapView’s AnnotationOrchestrator. Use the AnnotationOrchestrator to create annotation managers based on the type of annotation that you're interested in. Every annotation manager handles a collection of annotations. Once a manager has been created, you can create and add individually styled instances of the corresponding annotation type.

Benefits:

  • Built-in interaction support like selecting and dragging annotations around the map.
  • No external data file necessary.
  • Every annotation can be individually styled.
  • Every annotation layer can be adjusted to be above or below another layer.
  • Same performance benefits as using style layers.
  • Similar interface to MapKit’s annotation implementation.
  • Default marker image available.

Limitations:

  • Inefficient for adding many features to the map.

Default markers

A PointAnnotation can display a default red marker pin at a developer-specified geographic coordinate.

// Wait for the map's style to finish loading.
mapView.mapboxMap.onNext(.mapLoadingFinished) { [weak self] _ in

    guard let self = self else { return }

    // Initialize a point annotation with a geometry ("coordinate" in this case)
    // and configure it with the default image.
    var pointAnnotation = PointAnnotation(coordinate: someCoordinate)

    // Make the annotation show the default red pin
    pointAnnotation.image = .default

    // Create the `PointAnnotationManager` which will be responsible for handling this annotation (and store it to keep it alive)
    self.pointAnnotationManager = self.mapView.annotations.makePointAnnotationManager()

    // Add the annotation to the manager in order to render it on the map.
    self.pointAnnotationManager.syncAnnotations([pointAnnotation])
}
example
Add a default marker to the map

Use MapView's AnnotationOrchestrator class to add a single default red marker pin to the map using the Maps SDK for iOS.

Use a custom image

In addition to using a default red marker pin, you can also specfy a custom image to be added to the map using point annotations. Save the image in your Xcode project's assets folder and specify it by name as the point annotation's image.

let image = UIImage(named: "star")
var pointAnnotation = PointAnnotation(coordinate: customCoordinate)
pointAnnotation.image = .custom(image: image, name: "my-custom-image")

Other shapes

MapView’s AnnotationOrchestrator also supports putting other shapes on the map including circles using CircleAnnotationManager, polylines using PolylineAnnotationManager, and polygons using PolygonAnnotationManager. These annotations work like the point annotations described above, but do not require an image. The options available for each type of annotation varies and you can find a full list in the API reference documentation.

A circle annotation (CircleAnnotation) places a circle at a point on the map.

// Wait for the map's style to finish loading.
mapView.mapboxMap.onNext(.mapLoaded) { [weak self] _ in
    guard let self = self else { return }
    // Define a geographic coordinate.
    var circleCoordinates = CLLocationCoordinate2DMake(40.7128, -74.0060)

    // Create the circle annotation.
    var circleAnnotation = CircleAnnotation(coordinate: lineCoordinates)
    circleAnnotation.circleColor = ColorRepresentable(color: .red)

    // Create the `CircleAnnotationManager` which will be responsible for handling this annotation (and store it to keep it alive)
    self.circleAnnnotationManager = self.mapView.annotations.makeCircleAnnotationManager()

    // Sync the annotation to the manager.
    self.circleAnnnotationManager.syncAnnotations([circleAnnotation])
}

A polyline annotation (PolylineAnnotation) connects a list of coordinates on the map with a polyline. The order of the coordinates in the list will determine the order in which to connect the points, in the same way that coordinates are handled in the GeoJSON specification.

// Wait for the map's style to finish loading.
mapView.mapboxMap.onNext(.mapLoaded) { [weak self] _ in
    guard let self = self else { return }
    // Define two or more geographic coordinates to connect.
    // Line from New York City, NY to Washington, D.C.
    let lineCoordinates = [
        CLLocationCoordinate2DMake(40.7128, -74.0060),
        CLLocationCoordinate2DMake(38.9072, -77.0369)
    ]

    // Create the line annotation.
    var lineAnnotation = PolylineAnnotation(lineCoordinates: lineCoordinates)
    lineAnnotation.lineColor = ColorRepresentable(color: .red)

    // Create the `PolylineAnnotationManager` which will be responsible for handling this annotation (and store it to keep it alive)
    self.lineAnnnotationManager = self.mapView.annotations.makePolylineAnnotationManager()

    // Sync the annotation to the manager.
    self.lineAnnnotationManager.syncAnnotations([lineAnnotation])
}

A polygon annotation (PolygonAnnotation) takes a list of coordinates and will try to connect those coordinates and add the resulting polygonal shape to the map. The order of the coordinates in the list matters and works the same way as in the GeoJSON specification.

// Wait for the map's style to finish loading.
mapView.mapboxMap.onNext(.mapLoaded) { [weak self] _ in
    guard let self = self else { return }
    // Define three or more geographic coordinates to connect.
    let ringCoords = [
        CLLocationCoordinate2DMake(24.5171, -89.8571),
        CLLocationCoordinate2DMake(24.5171, -87.9675),
        CLLocationCoordinate2DMake(26.2441, -87.9675),
        CLLocationCoordinate2DMake(26.2441, -89.8571),
        CLLocationCoordinate2DMake(24.5171, -89.8571)
    ]
    let ring = Turf.Ring(coordinates: ringCoords)
    let polygon = Turf.Polygon(outerRing: ring)
    
    // Create a new polygon annotation using those coordinates.
    let polygonAnnotation = PolygonAnnotation(polygon: polygon)

    // Create the `PolygonAnnotationManager` which will be responsible for handling this annotation (and store it to keep it alive)
    self.polygonAnnotationManager = self.mapView.annotations.makePolygonAnnotationManager()
    
    // Add the polygon to the map as an annotation.
    self.polygonAnnotationManager.syncAnnotations([polygonAnnotation])
}

Interactivity

You can choose to receive tap events on annotations that you've added to a manager, by conforming to AnnotationInteractionDelegate (and setting yourself as the delegate on the manager).

class ViewController: UIViewController {
    internal var mapView: MapView!

    var pointAnnotationManager: PointAnnotationManager?

    override func viewDidLoad() {
        super.viewDidLoad()
        mapView.on(.mapLoadingFinished) { [weak self] (event) in
            guard let self = self else { return }
            
            // Make self the `AnnotationInteractionDelegate` to get called back on tap events
            self.pointAnnotationManager.delegate = self
            let coordinate = CLLocationCoordinate2DMake(24, -89)
            var pointAnnotation = PointAnnotation(coordinate: coordinate)
            pointAnnotation.image = .default

            self.pointAnnotationManager = self.mapView.annotations.makePointAnnotationManager()
            self.pointAnnotationManager.syncAnnotations([pointAnnotation])
        }
    }
}

extension ViewController: AnnotationInteractionDelegate {
    public func annotationManager(_ manager: AnnotationManager, didDetectTappedAnnotations annotations: [Annotation]) {
        print("Annotations tapped: \(annotations)")
    }
}

Style layers

Using annotations as described above makes it unnecessary to write much of the code that would otherwise be required to implement runtime and data-driven styling. But, it is possible to add style layers directly, and in some circumstances doing so will be beneficial.

Benefits:

  • Efficient and performant when adding many features to the map.
  • Compatible with GeoJSON or vector sources.
  • Many style customization options.

Limitations:

  • No default image available.
  • Most user interactions are not built-in and will require writing custom code.
  • Need to learn the APIs and usage of layers which can be time intensive.
  • Might need to learn how to use expressions to control the source data or use data-driven styling.
related
Style layer API reference

API reference documentation for adding style layers using the Maps SDK.