
Change a map viewpoint with MapKit
Discover how to change a MapKit map's camera position within a SwiftUI app.
Interactive maps are a key part of many travel, delivery, or navigation apps, where there's often the need to display geographical data. With the introduction of iOS 17, SwiftUI gained a new MapCamera
API, as part of the MapKit framework, designed to give developers full programmatic control over the map camera.
In this article, we’ll walk through how to use MapCamera
and MapCameraPosition
in SwiftUI. To illustrate it, we will create a simple map experience where the user can explore various cities worldwide by long-pressing on the map, getting a random town every time.
import SwiftUI
import MapKit
struct MapLocation: Identifiable, Equatable {
let id = UUID()
let name: String
let coordinate: CLLocationCoordinate2D
static func == (lhs: MapLocation, rhs: MapLocation) -> Bool {
lhs.id == rhs.id
}
}
enum City: CaseIterable {
case paris
case newYork
case tokyo
case naples
case berlin
var mapLocation: MapLocation {
var name: String
var coordinate: CLLocationCoordinate2D
switch self {
case .paris:
name = "Paris"
coordinate = CLLocationCoordinate2D(latitude: 48.858844, longitude: 2.294351)
case .newYork:
name = "New York"
coordinate = CLLocationCoordinate2D(latitude: 40.7128, longitude: -74.0060)
case .tokyo:
name = "Tokyo"
coordinate = CLLocationCoordinate2D(latitude: 35.6895, longitude: 139.6917)
case .naples:
name = "Naples"
coordinate = CLLocationCoordinate2D(latitude: 40.841235, longitude: 14.2551)
case .berlin:
name = "Berlin"
coordinate = CLLocationCoordinate2D(latitude: 52.520008, longitude: 13.40495)
}
return MapLocation(name: name, coordinate: coordinate)
}
}
struct ContentView: View {
@State private var selectedLocation: MapLocation = City.paris.mapLocation
@State private var isPresented = false
@State private var cameraPosition: MapCameraPosition = .camera(
MapCamera(
centerCoordinate: City.tokyo.mapLocation.coordinate,
distance: 2000,
heading: 0,
pitch: 45
)
)
var body: some View {
VStack {
Map(position: $cameraPosition) {
ForEach(City.allCases.map(\.mapLocation)) { mapLocation in
Marker(mapLocation.name, coordinate: mapLocation.coordinate)
}
}
.mapStyle(.standard)
// Long press gesture changes the selected location randomly
.onLongPressGesture {
selectedLocation = City.allCases.randomElement()!.mapLocation
}
// Once the selected location changes, updates the map camera position
.onChange(of: selectedLocation) { _, newLocation in
cameraPosition = MapCameraPosition.camera(
MapCamera(
centerCoordinate: newLocation.coordinate,
distance: 2000,
heading: 0,
pitch: 45
)
)
}
}
}
}
In the example above, the cameraPosition
property is used to precisely control the viewpoint of the Map
View, representing the state of the map’s camera. By utilizing the camera(_:)
method, which requires a parameter of the MapCamera
type, we can define the camera’s center coordinate, distance from the ground, heading, and pitch.
Initially centered on Tokyo, the camera dynamically updates when a new city is selected, triggered by a long-press gesture, updating the cameraPosition
property with a MapCamera
value.
This is one of the different possibilities that the MapCameraPosition
object offers to set the viewpoint of the map, according to our requirements. Here’s the complete list of these methods:
item(_:allowsAutomaticPitch:)
: creates a new camera position centered on a map item and automatically selects the pitch you provide.rect(_:)
: creates a new camera position based on the map boundaries you specify..region(_:)
: creates a new camera position based on the coordinate region you provide.userLocation(followsHeading:fallback:)
: creates a camera position with a specific fallback position and optionally tracks the user’s heading.