MapKit en SwiftUI recibió una renovación completa con iOS 17. La nueva API declarativa con Map, Marker, Annotation y los overlays integrados permite construir mapas ricos en pocas lÃneas, sin necesidad de recurrir a UIViewRepresentable para la mayorÃa de casos de uso.
Map con MapCameraPosition
El contenedor principal es Map. Para controlar la región visible y la cámara, se usa MapCameraPosition:
import MapKit
import SwiftUI
struct MapaBasico: View {
@State private var posicion: MapCameraPosition = .region(
MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 40.4168, longitude: -3.7038),
span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
)
)
var body: some View {
Map(position: $posicion)
.ignoresSafeArea()
}
}
Otras opciones de posición: .camera(MapCamera(centerCoordinate:distance:heading:pitch:)) para una cámara 3D, .automatic para que MapKit decida y .userLocation(fallback:) para centrar en el usuario.
Marker y Annotation: marcadores personalizados
Marker muestra un pin estándar del sistema con un icono opcional. Annotation permite una vista SwiftUI completamente personalizada:
struct PuntoInteres: Identifiable {
let id = UUID()
let nombre: String
let coordenadas: CLLocationCoordinate2D
let categoria: String
}
struct MapaPOI: View {
let puntos: [PuntoInteres]
@State private var posicion: MapCameraPosition = .automatic
var body: some View {
Map(position: $posicion) {
ForEach(puntos) { punto in
// Marker: pin estándar con tÃtulo e icono SF Symbol
Marker(punto.nombre, systemImage: iconoPara(punto.categoria),
coordinate: punto.coordenadas)
.tint(colorPara(punto.categoria))
// O Annotation para vista personalizada
Annotation(punto.nombre, coordinate: punto.coordenadas) {
VStack(spacing: 0) {
Image(systemName: iconoPara(punto.categoria))
.padding(8)
.background(.white)
.clipShape(Circle())
.shadow(radius: 4)
Image(systemName: "triangle.fill")
.font(.caption)
.foregroundStyle(.white)
.rotationEffect(.degrees(180))
.offset(y: -3)
}
}
}
}
}
func iconoPara(_ categoria: String) -> String {
switch categoria {
case "restaurante": return "fork.knife"
case "museo": return "building.columns"
default: return "mappin"
}
}
}
Overlays: MapCircle y MapPolyline
Para dibujar áreas o rutas sobre el mapa, iOS 17 añade MapCircle, MapPolygon y MapPolyline como contenido declarativo del Map:
Map {
// CÃrculo de 500 metros de radio
MapCircle(
center: CLLocationCoordinate2D(latitude: 40.4168, longitude: -3.7038),
radius: 500
)
.foregroundStyle(.blue.opacity(0.2))
.stroke(.blue, lineWidth: 2)
// Ruta como polilÃnea
let puntos = ruta.map { CLLocationCoordinate2D(latitude: $0.lat, longitude: $0.lon) }
MapPolyline(coordinates: puntos)
.stroke(.red, style: StrokeStyle(lineWidth: 4, lineCap: .round, lineJoin: .round))
}
Búsqueda de lugares: MKLocalSearch
MKLocalSearch permite buscar puntos de interés por texto o categorÃa:
func buscarLugares(texto: String, region: MKCoordinateRegion) async -> [MKMapItem] {
let peticion = MKLocalSearch.Request()
peticion.naturalLanguageQuery = texto
peticion.region = region
peticion.resultTypes = [.pointOfInterest, .address]
do {
let respuesta = try await MKLocalSearch(request: peticion).start()
return respuesta.mapItems
} catch {
print("Error en búsqueda: (error)")
return []
}
}
// Buscar por categorÃa (iOS 18+)
let peticionCategoria = MKLocalSearch.Request()
peticionCategoria.pointOfInterestFilter = MKPointOfInterestFilter(including: [.restaurant, .cafe])
peticionCategoria.region = regionActual
Look Around preview
Look Around es el equivalente de Apple a Street View. Se activa con MKLookAroundSceneRequest:
struct VistaLookAround: View {
let coordenadas: CLLocationCoordinate2D
@State private var escena: MKLookAroundScene?
@State private var mostrar = false
var body: some View {
VStack {
if let escena {
LookAroundPreview(scene: $escena)
.frame(height: 200)
.cornerRadius(12)
}
Button("Ver en Look Around") { mostrar = true }
}
.task {
let peticion = MKLookAroundSceneRequest(coordinate: coordenadas)
escena = try? await peticion.scene
}
.lookAroundViewer(isPresented: $mostrar, scene: $escena)
}
}
Selección de anotaciones
iOS 17 añade soporte nativo para selección de anotaciones con el parámetro selection:
@State private var seleccionado: PuntoInteres?
Map(position: $posicion, selection: $seleccionado) {
ForEach(puntos) { punto in
Marker(punto.nombre, coordinate: punto.coordenadas)
.tag(punto) // Necesario para que selection funcione
}
}
.onChange(of: seleccionado) { _, nuevo in
if let nuevo {
print("Seleccionado: (nuevo.nombre)")
}
}
Resumen
MapKit en iOS 17 con SwiftUI convierte en declarativo lo que antes requerÃa delegate y UIViewRepresentable. Map con MapCameraPosition controla la vista; Marker y Annotation añaden puntos personalizados; MapCircle y MapPolyline dibujan overlays; MKLocalSearch busca lugares por texto o categorÃa; y Look Around añade inmersión visual con pocas lÃneas. Para la mayorÃa de apps de mapas, la nueva API cubre el cien por cien de los casos de uso sin salir de SwiftUI.
