Map Display
The Map Component
SwiftUI provides a Map component that integrates with MapKit to display interactive maps. You can configure the map position, camera, and appearance using declarative syntax.
import SwiftUI
import MapKit
struct MapDemoView: View {
@State private var position: MapCameraPosition = .region(
MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
)
)
var body: some View {
Map(position: $position) { }
.mapControls {
MapUserLocationButton()
MapCompass()
MapScaleView()
MapPitchToggle()
}
}
}
Configuring Map Position
The map position uses MapCameraPosition to control what region is visible. You can set a fixed region, a 3D camera, or follow the user.
let region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 40.7128, longitude: -74.0060),
span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
)
Map(position: .constant(.region(region))) { }
// 3D camera view
Map(position: .constant(.camera(
MapCamera(
centerCoordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
distance: 1000, heading: 45, pitch: 60
)
))) { }
Map Appearance
Customize the map visual style using the mapStyle modifier with .standard, .imagery, or .hybrid options.
Map(position: $position) { }
.mapStyle(.hybrid(elevation: .realistic))
Interactive Maps
Handle user interactions by responding to taps and toolbar actions to manipulate the map view programmatically.
struct InteractiveMapView: View {
@State private var position: MapCameraPosition = .automatic
@State private var selectedLocation: CLLocationCoordinate2D?
var body: some View {
Map(position: $position) {
if let location = selectedLocation {
Marker("Selected", systemImage: "mappin.circle.fill", coordinate: location)
.tint(.red)
}
}
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button("Reset") {
position = .region(MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 0, longitude: 0),
span: MKCoordinateSpan(latitudeDelta: 180, longitudeDelta: 180)
))
}
}
}
}
}
Annotations & Overlays
Adding Markers
Markers are the simplest way to annotate points on a map. They display a standard pin with a title at a specific coordinate.
struct AnnotatedMapView: View {
@State private var position: MapCameraPosition = .region(
MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
)
)
let landmarks = [
(name: "Golden Gate Bridge", lat: 37.8199, lon: -122.4783),
(name: "Alcatraz Island", lat: 37.8267, lon: -122.4230),
(name: "Fishermans Wharf", lat: 37.8080, lon: -122.4177)
]
var body: some View {
Map(position: $position) {
ForEach(landmarks, id: \.name) { landmark in
Marker(landmark.name, systemImage: "mappin.circle.fill",
coordinate: CLLocationCoordinate2D(latitude: landmark.lat, longitude: landmark.lon))
.tint(.red)
}
}
}
}
Custom Annotations
Use Annotation to display custom SwiftUI views at map coordinates for full control over appearance.
Annotation("My Location", coordinate: myCoord) {
VStack(spacing: 0) {
Text("Home")
.font(.caption)
.padding(6)
.background(.blue)
.foregroundColor(.white)
.cornerRadius(8)
Image(systemName: "mappin.circle.fill")
.font(.title)
.foregroundColor(.blue)
}
}
Map Overlays
Overlays draw shapes and routes on the map. SwiftUI provides MapPolyline, MapCircle, and MapPolygon.
let route = [
CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
CLLocationCoordinate2D(latitude: 37.8044, longitude: -122.2712),
CLLocationCoordinate2D(latitude: 37.8199, longitude: -122.4783)
]
Map(position: $position) {
MapPolyline(coordinates: route)
.stroke(.blue, lineWidth: 4)
MapCircle(center: route[0], radius: 500)
.fill(.red.opacity(0.3))
MapPolygon(coordinates: [
CLLocationCoordinate2D(latitude: 37.78, longitude: -122.42),
CLLocationCoordinate2D(latitude: 37.79, longitude: -122.41),
CLLocationCoordinate2D(latitude: 37.78, longitude: -122.40)
]).fill(.green.opacity(0.3))
}
User Location
Requesting Location Permissions
Before accessing user location, you must request permission. Add NSLocationWhenInUseUsagePermission to your Info.plist.
import CoreLocation
class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
private let manager = CLLocationManager()
@Published var userLocation: CLLocation?
@Published var authStatus: CLAuthorizationStatus = .notDetermined
override init() {
super.init()
manager.delegate = self
}
func requestPermission() {
manager.requestWhenInUseAuthorization()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
userLocation = locations.last
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
authStatus = manager.authorizationStatus
}
}
Displaying User Location
Combine LocationManager with Map to show the user position on the map.
struct UserLocationMapView: View {
@StateObject private var locationManager = LocationManager()
@State private var position: MapCameraPosition = .userLocation(fallback: .automatic)
var body: some View {
Map(position: $position) {
UserAnnotation()
}
.mapControls { MapUserLocationButton() }
.onAppear { locationManager.requestPermission() }
}
}
Forward and Reverse Geocoding
MapKit includes geocoding services to convert between coordinates and human-readable addresses using MKLocalSearch and CLGeocoder.
func geocodeAddress(_ address: String) async throws -> MKMapItem {
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = address
let search = MKLocalSearch(request: request)
let response = try await search.start()
guard let item = response.mapItems.first else {
throw NSError(domain: "GeocodingError", code: 0)
}
return item
}
func reverseGeocode(location: CLLocation) async throws -> String {
let geocoder = CLGeocoder()
let placemarks = try await geocoder.reverseGeocodeLocation(location)
return placemarks.first?.name ?? "Unknown Location"
}
Quiz
1. What does MapCameraPosition control in SwiftUI maps?
2. What Info.plist key is required for foreground location access?
3. Which SwiftUI component draws a line between coordinates on a map?
4. How do you display the user location on a Map in SwiftUI?
Flashcards
Question
What is MapCameraPosition used for?
Click to reveal answer
Answer
It controls the visible region, camera angle, heading, and pitch of a SwiftUI Map view.
Question
What is the difference between Marker and Annotation?
Click to reveal answer
Answer
Marker provides a standard pin with title. Annotation allows custom view content at a coordinate.
Question
What overlay type draws a route on a map?
Click to reveal answer
Answer
MapPolyline draws connected line segments between an array of coordinates.
Question
How do you request foreground location permission?
Click to reveal answer
Answer
Call locationManager.requestWhenInUseAuthorization() and add NSLocationWhenInUseUsagePermission to Info.plist.
Revision Notes
Key Takeaways
- 1. MapCameraPosition controls the visible region and camera
- 2. Markers are simple pins; Annotations allow custom views
- 3. MapPolyline, MapCircle, and MapPolygon draw overlays
- 4. UserAnnotation() displays the user location
- 5. Location permissions are required before accessing user location
Interview Tips
- • Explain the difference between MapCameraPosition and MKCoordinateRegion
- • Discuss when to use Annotation vs Marker
- • Describe how to handle location permission flow
Cheat Sheet
MapKit Quick Reference
- Map(position:) - Main map view with camera position binding
- MapCameraPosition - Controls visible region (.region, .camera, .userLocation)
- Marker - Standard pin annotation
- Annotation - Custom view annotation
- MapPolyline/MapCircle/MapPolygon - Map overlays
- UserAnnotation() - Displays user location
- .mapStyle() - Set map visual style
- .mapControls {} - Add standard map controls