MKMapView
annotations
iOS development
Swift
map positioning

Positioning MKMapView to show multiple annotations at once

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

To position an MKMapView so all annotations are visible, use showAnnotations(_:animated:) for a quick solution, or compute an MKCoordinateRegion from annotation coordinates for precise control over padding and zoom. For complex cases, calculate an MKMapRect that encloses all annotation points and call setVisibleMapRect(_:edgePadding:animated:) to account for UI elements overlapping the map edges.

showAnnotations (Simplest Approach)

swift
1import MapKit
2
3let mapView = MKMapView()
4
5// Add annotations
6let annotations = [
7    MKPointAnnotation(__coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194), title: "San Francisco", subtitle: nil),
8    MKPointAnnotation(__coordinate: CLLocationCoordinate2D(latitude: 34.0522, longitude: -118.2437), title: "Los Angeles", subtitle: nil),
9    MKPointAnnotation(__coordinate: CLLocationCoordinate2D(latitude: 36.1699, longitude: -115.1398), title: "Las Vegas", subtitle: nil),
10]
11
12mapView.addAnnotations(annotations)
13
14// Zoom to show all annotations
15mapView.showAnnotations(annotations, animated: true)

showAnnotations automatically calculates a region that fits all provided annotations with some default padding. It is the simplest solution but offers no control over edge insets.

Custom Region Calculation

For precise control over the visible region:

swift
1func zoomToFitAnnotations(mapView: MKMapView, annotations: [MKAnnotation], padding: Double = 0.1) {
2    guard !annotations.isEmpty else { return }
3
4    var minLat = annotations[0].coordinate.latitude
5    var maxLat = annotations[0].coordinate.latitude
6    var minLon = annotations[0].coordinate.longitude
7    var maxLon = annotations[0].coordinate.longitude
8
9    for annotation in annotations {
10        let lat = annotation.coordinate.latitude
11        let lon = annotation.coordinate.longitude
12        minLat = min(minLat, lat)
13        maxLat = max(maxLat, lat)
14        minLon = min(minLon, lon)
15        maxLon = max(maxLon, lon)
16    }
17
18    let latDelta = (maxLat - minLat) * (1 + padding)
19    let lonDelta = (maxLon - minLon) * (1 + padding)
20
21    let center = CLLocationCoordinate2D(
22        latitude: (minLat + maxLat) / 2,
23        longitude: (minLon + maxLon) / 2
24    )
25
26    let span = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lonDelta)
27    let region = MKCoordinateRegion(center: center, span: span)
28
29    mapView.setRegion(region, animated: true)
30}

The padding parameter adds extra space around the annotations (0.1 = 10% padding on each side).

Using MKMapRect with Edge Padding

For the most precise control, especially when UI elements overlap map edges:

swift
1func zoomToAnnotations(mapView: MKMapView, annotations: [MKAnnotation], edgePadding: UIEdgeInsets) {
2    guard !annotations.isEmpty else { return }
3
4    var mapRect = MKMapRect.null
5
6    for annotation in annotations {
7        let point = MKMapPoint(annotation.coordinate)
8        let pointRect = MKMapRect(x: point.x, y: point.y, width: 0.1, height: 0.1)
9        mapRect = mapRect.union(pointRect)
10    }
11
12    mapView.setVisibleMapRect(mapRect, edgePadding: edgePadding, animated: true)
13}
14
15// Usage with padding for a navigation bar and tab bar
16zoomToAnnotations(
17    mapView: mapView,
18    annotations: mapView.annotations,
19    edgePadding: UIEdgeInsets(top: 100, left: 50, bottom: 100, right: 50)
20)

setVisibleMapRect(_:edgePadding:animated:) is the most flexible option because it accounts for UI overlays (navigation bars, toolbars, floating buttons) that reduce the visible map area.

Including User Location

swift
1func zoomToAnnotationsAndUser(mapView: MKMapView) {
2    var annotations = mapView.annotations
3
4    // Include the user's location as an annotation
5    if let userLocation = mapView.userLocation.location {
6        let userAnnotation = MKPointAnnotation()
7        userAnnotation.coordinate = userLocation.coordinate
8        annotations.append(userAnnotation)
9    }
10
11    mapView.showAnnotations(annotations, animated: true)
12}

Filtering Annotations

swift
1// Show only specific annotation types
2let pointAnnotations = mapView.annotations.filter { annotation in
3    annotation is MKPointAnnotation  // Exclude MKUserLocation
4}
5
6mapView.showAnnotations(pointAnnotations, animated: true)

Setting a Minimum Zoom Level

When annotations are close together, the map may zoom in too far:

swift
1func zoomToAnnotations(mapView: MKMapView, annotations: [MKAnnotation], minSpanDelta: Double = 0.01) {
2    guard !annotations.isEmpty else { return }
3
4    if annotations.count == 1 {
5        // Single annotation: center with a fixed span
6        let region = MKCoordinateRegion(
7            center: annotations[0].coordinate,
8            span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
9        )
10        mapView.setRegion(region, animated: true)
11        return
12    }
13
14    var mapRect = MKMapRect.null
15    for annotation in annotations {
16        let point = MKMapPoint(annotation.coordinate)
17        let rect = MKMapRect(x: point.x, y: point.y, width: 0.1, height: 0.1)
18        mapRect = mapRect.union(rect)
19    }
20
21    // Ensure minimum visible area
22    let minSize = MKMapPoint(CLLocationCoordinate2D(latitude: minSpanDelta, longitude: minSpanDelta))
23    if mapRect.size.width < minSize.x {
24        mapRect = mapRect.insetBy(dx: -(minSize.x - mapRect.size.width) / 2, dy: 0)
25    }
26    if mapRect.size.height < minSize.y {
27        mapRect = mapRect.insetBy(dx: 0, dy: -(minSize.y - mapRect.size.height) / 2)
28    }
29
30    mapView.setVisibleMapRect(mapRect, edgePadding: UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50), animated: true)
31}

Animating After Data Loads

swift
1class MapViewController: UIViewController, MKMapViewDelegate {
2    @IBOutlet weak var mapView: MKMapView!
3
4    func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {
5        // Zoom to fit after annotations are rendered
6        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
7            mapView.showAnnotations(mapView.annotations, animated: true)
8        }
9    }
10}

Common Pitfalls

  • Calling showAnnotations before annotations are added: showAnnotations uses the provided array, not the map's current annotations. If you pass an empty array, the map does not zoom. Add annotations to the map first or pass the correct array.
  • Forgetting to exclude MKUserLocation: mapView.annotations includes the user's blue dot as MKUserLocation. If the user is far from your pins, the map zooms out excessively. Filter with annotations.filter { !($0 is MKUserLocation) }.
  • Single annotation zooms to maximum level: With one annotation, showAnnotations may zoom in extremely close. Check for annotations.count == 1 and use a fixed span instead.
  • Edge padding not accounting for safe areas: On devices with notches, the safe area insets reduce usable space. Add view.safeAreaInsets to your edge padding for accurate positioning.
  • Coordinate span of zero: If all annotations have the exact same coordinate, the lat/lon deltas are zero, which causes undefined behavior in setRegion. Always enforce a minimum span delta.

Summary

  • Use mapView.showAnnotations(annotations, animated: true) for the simplest fit-all approach
  • Calculate MKCoordinateRegion manually for control over padding percentage
  • Use setVisibleMapRect(_:edgePadding:animated:) when UI elements overlap the map edges
  • Filter out MKUserLocation if you only want to zoom to custom annotations
  • Handle single-annotation and same-coordinate edge cases with minimum span values

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.