πΊοΈ Mastering MapKit
Build location-based apps with Apple Maps β markers, routes, 3D buildings and more!
β Difficulty: ββ
β±οΈ Est. Time: 1-2h
π App Services
β¨ MapKit is?
MapKit is a framework for integrating Apple Maps into your app. Easily implemented with SwiftUI's Map view.
πΊοΈ Basic Map Display (SwiftUI)
BasicMap.swift
import SwiftUI import MapKit struct ContentView: View { @State private var position: MapCameraPosition = .region( MKCoordinateRegion( center: CLLocationCoordinate2D(latitude: 37.5665, longitude: 126.9780), span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05) ) ) var body: some View { Map(position: $position) } } // Map μ€νμΌ Map(position: $position) { } .mapStyle(.standard) // .standard, .hybrid, .imagery
π Adding Markers
Markers.swift
import SwiftUI import MapKit struct Location: Identifiable { let id = UUID() let name: String let coordinate: CLLocationCoordinate2D } struct MapWithMarkers: View { @State private var position: MapCameraPosition = .automatic let locations = [ Location(name: "경볡κΆ", coordinate: CLLocationCoordinate2D(latitude: 37.5788, longitude: 126.9770)), Location(name: "λ¨μ°νμ", coordinate: CLLocationCoordinate2D(latitude: 37.5512, longitude: 126.9882)), Location(name: "νκ°κ³΅μ", coordinate: CLLocationCoordinate2D(latitude: 37.5326, longitude: 126.9610)) ] var body: some View { Map(position: $position) { ForEach(locations) { location in // Default λ§μ»€ Marker(location.name, coordinate: location.coordinate) } } } } // Custom μμ λ§μ»€ Marker("λ μ€ν λ", systemImage: "fork.knife", coordinate: coord) .tint(.red) // μ΄λ Έν μ΄μ (λ λ§μ 컀μ€ν°λ§μ΄μ§) Annotation("μΉ΄ν", coordinate: coord) { ZStack { Circle() .fill(Color.blue) .frame(width: 30, height: 30) Image(systemName: "cup.and.saucer.fill") .foregroundColor(.white) } }
π― Displaying User Location
UserLocation.swift
import SwiftUI import MapKit struct MapWithUserLocation: View { @State private var position: MapCameraPosition = .userLocation(fallback: .automatic) var body: some View { Map(position: $position) { UserAnnotation() // User μμΉ λ§μ»€ } .mapControls { MapUserLocationButton() // λ΄ μμΉ λ²νΌ MapCompass() // λμΉ¨λ° MapScaleView() // μΆμ² } } }
π£οΈ Showing Directions
Route.swift
import MapKit func calculateRoute(from start: CLLocationCoordinate2D, to end: CLLocationCoordinate2D) async throws -> MKRoute? { let request = MKDirections.Request() request.source = MKMapItem(placemark: MKPlacemark(coordinate: start)) request.destination = MKMapItem(placemark: MKPlacemark(coordinate: end)) request.transportType = .automobile // .walking, .transit let directions = MKDirections(request: request) let response = try await directions.calculate() return response.routes.first } // SwiftUIμμ κ²½λ‘ νμ struct RouteMapView: View { @State private var route: MKRoute? @State private var position: MapCameraPosition = .automatic var body: some View { Map(position: $position) { if let route = route { MapPolyline(route.polyline) .stroke(Color.blue, lineWidth: 5) } // μΆλ°/λμ°© λ§μ»€ Marker("μΆλ°", systemImage: "mappin.circle.fill", coordinate: start) .tint(.green) Marker("λμ°©", systemImage: "flag.fill", coordinate: end) .tint(.red) } .task { route = try? await calculateRoute(from: start, to: end) } } }
π Place Search
Search.swift
import MapKit func searchPlaces(query: String, region: MKCoordinateRegion) async throws -> [MKMapItem] { let request = MKLocalSearch.Request() request.naturalLanguageQuery = query // "μΉ΄ν", "λ μ€ν λ" λ± request.region = region let search = MKLocalSearch(request: request) let response = try await search.start() return response.mapItems } // SwiftUI ν΅ν© struct SearchMapView: View { @State private var searchResults: [MKMapItem] = [] @State private var searchText = "" @State private var position: MapCameraPosition = .automatic var body: some View { VStack { TextField("μ₯μ κ²μ", text: $searchText) .textFieldStyle(.roundedBorder) .padding() .onSubmit { Task { await search() } } Map(position: $position) { ForEach(searchResults, id: \.self) { item in Marker(item.name ?? "μ₯μ", coordinate: item.placemark.coordinate) } } } } func search() async { let region = MKCoordinateRegion( center: CLLocationCoordinate2D(latitude: 37.5665, longitude: 126.9780), span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1) ) searchResults = (try? await searchPlaces(query: searchText, region: region)) ?? [] } }
π Reverse Geocoding
Geocoding.swift
import CoreLocation func reverseGeocode(_ coordinate: CLLocationCoordinate2D) async throws -> String { let geocoder = CLGeocoder() let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude) let placemarks = try await geocoder.reverseGeocodeLocation(location) guard let placemark = placemarks.first else { return "μ£Όμ μμ" } let address = [ placemark.thoroughfare, // λλ‘λͺ placemark.locality, // μ/ꡬ placemark.country // κ΅κ° ].compactMap { $0 }.joined(separator: ", ") return address } // μ μ§μ€μ½λ© (μ£Όμ β μ’ν) func geocode(address: String) async throws -> CLLocationCoordinate2D? { let geocoder = CLGeocoder() let placemarks = try await geocoder.geocodeAddressString(address) return placemarks.first?.location?.coordinate }
π¨ Camera Control
CameraControl.swift
import SwiftUI import MapKit struct CameraControlView: View { @State private var position: MapCameraPosition = .automatic var body: some View { VStack { Map(position: $position) HStack { // νΉμ μ’νλ‘ μ΄λ Button("μμΈ") { position = .region( MKCoordinateRegion( center: CLLocationCoordinate2D(latitude: 37.5665, longitude: 126.9780), span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05) ) ) } // Camera eachλ μ‘°μ (3D) Button("3D λ·°") { position = .camera( MapCamera( centerCoordinate: CLLocationCoordinate2D(latitude: 37.5665, longitude: 126.9780), distance: 1000, // κ³ λ heading: 45, // λ°©ν₯ pitch: 60 // κΈ°μΈκΈ° ) ) } } .padding() } } }
π’ Map Styles & 3D Buildings
MapStyles.swift
Map(position: $position) { } .mapStyle(.standard) // Default .mapStyle(.hybrid) // μμ± + λλ‘ .mapStyle(.imagery) // μμ±λ§ // 3D 건물 νμ .mapStyle(.standard(elevation: .realistic)) // POI (κ΄μ¬ μ₯μ) νν° .mapStyle(.standard(pointsOfInterest: .including([.cafe, .restaurant])))
π± UIKit Integration (MKMapView)
MKMapView.swift
import SwiftUI import MapKit struct MapViewRepresentable: UIViewRepresentable { @Binding var region: MKCoordinateRegion func makeUIView(context: Context) -> MKMapView { let mapView = MKMapView() mapView.delegate = context.coordinator return mapView } func updateUIView(_ mapView: MKMapView, context: Context) { mapView.setRegion(region, animated: true) } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, MKMapViewDelegate { var parent: MapViewRepresentable init(_ parent: MapViewRepresentable) { self.parent = parent } func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) { parent.region = mapView.region } } }
π― Selectable Markers
SelectableMarkers.swift
struct SelectableMapView: View { @State private var selectedLocation: Location? @State private var position: MapCameraPosition = .automatic var body: some View { Map(position: $position, selection: $selectedLocation) { ForEach(locations) { location in Marker(location.name, coordinate: location.coordinate) .tag(location) } } .sheet(item: $selectedLocation) { location in VStack { Text(location.name) .font(.title) Text("μλ: \(location.coordinate.latitude)") Text("κ²½λ: \(location.coordinate.longitude)") } .padding() } } }
π‘ HIG Checklist
β
μ¬μ©μ μμΉ νμ μ Permission Request
β
Loading State Display
β
Network Error Handling
β
μ μ ν μ€ λ 벨 μ€μ
β
λ§μ»€λ λͺ
ννκ³ κ΅¬λΆ κ°λ₯νκ²