Bâtir des cartes interactives riches avec MapKit et Overlays dans iOS

Les cartes sont un élément central d'innombrables applications iOS, depuis le partage et la livraison de services jusqu'aux guides de voyage et aux traqueurs de fitness. Le cadre MapKit d'Apple offre aux développeurs une trousse d'outils puissante mais facile à utiliser pour intégrer des cartes et ajouter des éléments visuels personnalisés tels que les itinéraires, les limites et les superpositions de données en direct.

Configuration de MapKit dans votre projet

Avant de dessiner n'importe quoi sur une carte, vous devez préparer votre projet Xcode. Le processus comporte trois étapes rapides : importer le cadre, demander les autorisations de localisation, et ajouter un à votre interface.

Importer MapKit et demander les autorisations

Si votre application doit afficher l'emplacement actuel de l'utilisateur, vous devez également demander l'autorisation. Dans , ajoutez la clé avec une brève explication, comme - Pour afficher votre position sur la carte. Pour les versions iOS plus récentes, ajoutez aussi la clé si vous avez besoin de mises à jour de l'emplacement de l'arrière-plan, bien que pour la plupart des cas d'utilisation de la carte, -lorsque l'utilisation est suffisante.

Ajouter un MKMapView

Vous pouvez ajouter une vue de carte programmatique ou via Interface Builder. En utilisant Interface Builder, faites glisser une MapKit View[ de la bibliothèque d'objets sur votre toile de controller de vue. Connectez-la à une sortie :

@IBOutlet weak var mapView: MKMapView!

Pour créer la vue de carte entièrement en code, placez ceci à l'intérieur :

let mapView = MKMapView(frame: view.bounds)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(mapView)

Don-t oublions de définir le délégataire map-s à votre contrôleur de vue pour recevoir des callbacks pour les vues de rendu et d'annotation de superposition:

mapView.delegate = self

Choix d'une carte Type et région initiale

MapKit offre plusieurs types de cartes : standard, satellite, hybride et flyover.

mapView.mapType = .standard // or .satellite, .hybrid, .satelliteFlyover, .hybridFlyover

Pour définir une première région visible, définissez une coordonnée et une portée (latitude et longitude delta) et appelez :

let center = CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194)
let span = MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)
let region = MKCoordinateRegion(center: center, span: span)
mapView.setRegion(region, animated: false)

Pour une expérience utilisateur plus fluide, définissez une plage de temps plus appropriée en fonction des données que vous affichez. Vous pouvez également utiliser ou pour ajuster automatiquement la région pour s'adapter à toutes les superpositions et annotations.

Comprendre les superpositions dans MapKit

Contrairement aux annotations (qui sont des points simples avec un callout), les superpositions peuvent couvrir des formes arbitraires : lignes, polygones, cercles, ou même des images personnalisées. MapKit fournit trois types de superposition intégrés et un générique pour les serveurs de tuiles personnalisés. Voici un aperçu rapide :

  • MKPolyline[ – pour les itinéraires, les sentiers ou tout sentier composé de segments de ligne connectés.
  • MKPolygon – pour les zones remplies comme les quartiers, les parcs ou les zones de livraison.
  • MKCircle – pour les régions circulaires (p. ex., rayon de proximité autour d'un point).
  • MKTileOverlay – pour afficher des carrelages cartographiques personnalisés (p. ex. cartes de chaleur ou images satellitaires d'un fournisseur tiers).

Chaque superposition est ajoutée à la carte avec et son rendu est géré par la méthode de délégation .

Ajout d'un recouvrement en polyline

Un cas d'utilisation courant affiche une route. Créez un tableau de coordonnées, puis ininstantanez un :

let coordinates = [
 CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194), // San Francisco
 CLLocationCoordinate2D(latitude: 37.7849, longitude: -122.4094),
 CLLocationCoordinate2D(latitude: 37.7949, longitude: -122.3994)
]
let polyline = MKPolyline(coordinates: coordinates, count: coordinates.count)
mapView.addOverlay(polyline)

Lorsque vous ajoutez cette superposition, la carte appellera son délégué pour obtenir un rendu. Vous devez implémenter cette méthode; sinon aucune superposition n'apparaîtra (voir la section de rendu ci-dessous).

Ajout d'un superposition polygonale

Les polygones fonctionnent de la même manière mais créent une forme fermée. Par exemple, pour mettre en valeur un parc urbain :

let parkCoordinates = [
 CLLocationCoordinate2D(latitude: 37.7695, longitude: -122.4645),
 CLLocationCoordinate2D(latitude: 37.7595, longitude: -122.4495),
 CLLocationCoordinate2D(latitude: 37.7695, longitude: -122.4395),
 CLLocationCoordinate2D(latitude: 37.7795, longitude: -122.4545)
]
let polygon = MKPolygon(coordinates: parkCoordinates, count: parkCoordinates.count)
mapView.addOverlay(polygon)

Les polygones peuvent aussi avoir des trous intérieurs (sous-polygones), qui sont utiles pour montrer des limites avec des découpes comme une île à l'intérieur d'un lac.

Ajout d'un recouvrement de cercle

Les cercles ne nécessitent qu'une coordination centrale et un rayon en mètres:

let circle = MKCircle(center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194), radius: 5000) // 5 km radius
mapView.addOverlay(circle)

C'est parfait pour montrer des zones géofences ou de couverture.

Personnaliser l'apparence des superpositions avec les rendeurs

Le dessin réel des superpositions est fait par les rendus de superposition. Lorsque vous ajoutez une superposition à la carte, la méthode de délégation est appelée avec l'objet superposition. Vous retournez un rendu configuré avec la couleur de course, la couleur de remplissage, la largeur de ligne et d'autres propriétés.

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
 if let polyline = overlay as? MKPolyline {
 let renderer = MKPolylineRenderer(polyline: polyline)
 renderer.strokeColor = UIColor.systemBlue
 renderer.lineWidth = 4
 renderer.lineDashPattern = [10, 5] // dashed line
 return renderer
 } else if let polygon = overlay as? MKPolygon {
 let renderer = MKPolygonRenderer(polygon: polygon)
 renderer.fillColor = UIColor.systemGreen.withAlphaComponent(0.3)
 renderer.strokeColor = UIColor.systemGreen
 renderer.lineWidth = 2
 return renderer
 } else if let circle = overlay as? MKCircle {
 let renderer = MKCircleRenderer(circle: circle)
 renderer.fillColor = UIColor.systemOrange.withAlphaComponent(0.2)
 renderer.strokeColor = UIColor.systemOrange
 renderer.lineWidth = 1
 return renderer
 }
 // Fallback – should not happen if you handle all overlay types
 return MKOverlayRenderer(overlay: overlay)
}

Les propriétés clés que vous pouvez personnaliser incluent:

  • strokeColor, fillColor[ avec alpha pour la transparence.
  • LigneLidth pour les polylignes et les bords de polygone.
  • lineDashPattern[ – un tableau de nombres représentant les longueurs de tirets.
  • lineCap, lineJoindre pour arrondir les extrémités de la ligne.

Les soumissionnaires supportent également alpha et blendMode[ (bien que le mode de mélange soit limité par rapport aux graphiques de base). Pour les cercles, vous pouvez configurer radius (déjà défini à la création) mais vous ne pouvez pas le modifier après; vous devez supprimer et réajouter le cercle superposé.

Utilisation de superpositions de tuiles pour les données de carte personnalisées

Si vous souhaitez afficher un calque de raster personnalisé (par exemple un radar météorologique ou une carte historique), utilisez . Vous spécifiez un modèle d'URL pour le serveur de tuiles et définissez en option un niveau de zoom minimal/maximum:

let template = "https://tile.openweathermap.org/map/precipitation_new/{z}/{x}/{y}.png?appid=YOUR_API_KEY"
let tileOverlay = MKTileOverlay(urlTemplate: template)
tileOverlay.canReplaceMapContent = false // set true if the tiles should completely replace Apple maps
mapView.addOverlay(tileOverlay)

Pour rendre les recouvrements de tuiles, vous avez besoin d'une coutume :

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
 if let tileOverlay = overlay as? MKTileOverlay {
 return MKTileOverlayRenderer(tileOverlay: tileOverlay)
 }
 // ... other overlay renderers
}

Notez que les superpositions de tuiles utilisent requêtes URL qui doivent être servies sur HTTPS; Apple="s sécurité de transport bloquera HTTP par défaut à moins d'ajouter des exceptions.

Faire de la carte interactive

Une carte vraiment interactive va au-delà des lignes et formes statiques. Les utilisateurs s'attendent à taper sur des points d'intérêt, voir les appels, et peut-être même dessiner sur la carte eux-mêmes. Voici comment ajouter ces interactions.

Ajout d'annotations avec appels

Les annotations sont des points sur la carte qui peuvent afficher un titre, un sous-titre et une vue de callout lors de la tapotation. La manière la plus simple est d'utiliser :

let annotation = MKPointAnnotation()
annotation.title = "Golden Gate Bridge"
annotation.subtitle = "San Francisco, CA"
annotation.coordinate = CLLocationCoordinate2D(latitude: 37.8199, longitude: -122.4783)
mapView.addAnnotation(annotation)

Pour personnaliser l'annotation visuelle (couleur de la broche, image, callout), implémenter la méthode de délégation :

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
 guard !(annotation is MKUserLocation) else { return nil } // use default user location view
 let identifier = "PinAnnotation"
 var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKMarkerAnnotationView
 if annotationView == nil {
 annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
 } else {
 annotationView?.annotation = annotation
 }
 annotationView?.canShowCallout = true
 annotationView?.glyphText = "📍"
 annotationView?.markerTintColor = UIColor.systemRed
 // Add a detail disclosure button to the callout
 annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
 return annotationView
}

Le peut déclencher une segue ou afficher un panneau d'information lorsqu'il est tapé. Utilisez la méthode de délégation pour manipuler le robinet.

Ajouter des repérables de gestualisation pour le dessin ou la sélection

Pour laisser les utilisateurs dessiner sur la carte (par exemple, pour mesurer une route ou définir un polygone), attachez des reconnaissances de gestes à la vue de la carte. Une approche commune est l'utilisation d'un geste à longue pression pour déposer une broche :

let longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))
mapView.addGestureRecognizer(longPress)

@objc func handleLongPress(_ gesture: UILongPressGestureRecognizer) {
 guard gesture.state == .began else { return }
 let point = gesture.location(in: mapView)
 let coordinate = mapView.convert(point, toCoordinateFrom: mapView)
 let annotation = MKPointAnnotation()
 annotation.coordinate = coordinate
 annotation.title = "Custom Point"
 mapView.addAnnotation(annotation)
}

Pour un dessin plus avancé (p. ex., laisser les utilisateurs tracer un chemin qui devient une superposition), suivre le geste de la pan et accumuler les coordonnées, puis créer une polyligne ou un polygone après la fin du geste.

Annotations groupées pour la performance

Lorsque vous avez de nombreuses annotations (cents ou milliers), les afficher tous à la fois peut écraser les performances et encombrer la carte. MapKit supporte annotation cluster nativement. Pour l'activer, définissez le sur votre vue d'annotation:

annotationView?.clusteringIdentifier = "myCluster"

Vous pouvez également fournir une vue d'annotation de cluster personnalisée en retournant une vue dans le délégué. Le système regroupera automatiquement les annotations à proximité dans un cluster unique et mettra à jour lorsque l'utilisateur zoomera. Pour les gros ensembles de données, envisager d'utiliser ---méthode pour améliorer les performances de réutilisation.

Affichage de l'emplacement de l'utilisateur

Pour afficher l'emplacement actuel de l'utilisateur sur la carte, définissez . Vous devrez aussi demander les permissions de localisation comme discuté précédemment. Combinez ceci avec des superpositions pour afficher, par exemple, un cercle représentant l'utilisateur.

func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
 let region = MKCoordinateRegion(center: userLocation.coordinate, latitudinalMeters: 1000, longitudinalMeters: 1000)
 mapView.setRegion(region, animated: true)
 // Optionally add a circle overlay for accuracy
}

Notez que l'annotation de l'emplacement de l'utilisateur est une annotation spéciale; vous pouvez éviter de modifier sa vue ou de la supprimer.

Considérations de rendement avec de nombreuses superpositions

Ajouter des centaines ou des milliers de formes de recouvrement peut dégrader la défilement de la carte et zoomer les performances. Suivez ces conseils pour garder votre application réactive:

  • Simplify geometries – réduire le nombre de points de coordonnées dans les polylignes et les polygones. Pour les longues routes, utilisez un algorithme de simplification (p. ex. Ramer–Douglas–Peucker) pour supprimer les détails inutiles.
  • Utilisez des recouvrements de tuiles pour des données de grille denses (p. ex. cartes de chaleur) au lieu de milliers de petits cercles ou polygones qui se chevauchent.
  • Supprimer les superpositions lorsqu'elles sont hors écran – vous pouvez écouter les changements de région dans et ajouter/supprimer des superpositions basées sur le rectangle visible.
  • Batch ajouter et supprimer – utiliser et pour éviter les passages répétés de la disposition.
  • Considérer en utilisant – vous pouvez régler la propriété d'un rendu de recouvrement à ou pour contrôler l'ordre de dessin et éviter tout redessiner inutile.

Tout mettre en place : Exemple de carte interactive

Pour illustrer les concepts, voici un mini exemple d'une carte qui montre une route cyclable (polyligne) avec une annotation de début et de fin, ainsi qu'un cercle superposé pour indiquer un tampon de 500 mètres autour d'un point de cheminement. L'utilisateur peut appuyer sur les annotations pour voir les détails.

class RouteViewController: UIViewController, MKMapViewDelegate {
 @IBOutlet weak var mapView: MKMapView!

 override func viewDidLoad() {
 super.viewDidLoad()
 mapView.delegate = self
 mapView.mapType = .standard

 let startCoord = CLLocationCoordinate2D(latitude: 40.7128, longitude: -74.0060) // NYC
 let endCoord = CLLocationCoordinate2D(latitude: 40.7282, longitude: -73.7949) // near Central Park
 let waypointCoord = CLLocationCoordinate2D(latitude: 40.7220, longitude: -73.9000)

 // Add start and end annotations
 let startAnnotation = MKPointAnnotation()
 startAnnotation.title = "Start"
 startAnnotation.subtitle = "Downtown NYC"
 startAnnotation.coordinate = startCoord
 mapView.addAnnotation(startAnnotation)

 let endAnnotation = MKPointAnnotation()
 endAnnotation.title = "Finish"
 endAnnotation.subtitle = "Central Park North"
 endAnnotation.coordinate = endCoord
 mapView.addAnnotation(endAnnotation)

 // Create route polyline
 let routeCoords = [startCoord, waypointCoord, endCoord]
 let route = MKPolyline(coordinates: routeCoords, count: routeCoords.count)
 mapView.addOverlay(route)

 // Create a buffer circle around waypoint
 let buffer = MKCircle(center: waypointCoord, radius: 500)
 mapView.addOverlay(buffer)

 // Fit region to show all overlays
 mapView.showOverlays(mapView.overlays, animated: false)
 }

 // MARK: - MKMapViewDelegate

 func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
 if let polyline = overlay as? MKPolyline {
 let renderer = MKPolylineRenderer(polyline: polyline)
 renderer.strokeColor = UIColor.systemBlue
 renderer.lineWidth = 3
 renderer.lineDashPattern = [12, 4]
 return renderer
 } else if let circle = overlay as? MKCircle {
 let renderer = MKCircleRenderer(circle: circle)
 renderer.fillColor = UIColor.systemYellow.withAlphaComponent(0.2)
 renderer.strokeColor = UIColor.systemYellow
 renderer.lineWidth = 2
 return renderer
 }
 return MKOverlayRenderer(overlay: overlay)
 }

 func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
 guard !(annotation is MKUserLocation) else { return nil }
 let identifier = "RoutePin"
 var view = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKMarkerAnnotationView
 if view == nil {
 view = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
 } else {
 view?.annotation = annotation
 }
 view?.canShowCallout = true
 view?.markerTintColor = annotation.title == "Start" ? UIColor.systemGreen : UIColor.systemRed
 view?.glyphText = annotation.title == "Start" ? "🚴" : "🏁"
 return view
 }
}

Cet exemple montre comment les superpositions et les annotations fonctionnent ensemble, et comment les méthodes de délégation personnalisent l'apparence de la carte.

Ressources supplémentaires

MapKit est un cadre mature avec de nombreuses fonctionnalités supplémentaires non couvertes ici, telles que [[]]]][FLT][FLT]][FLT][FLT][FLT][FLT][FLT][FLT][FLT][FLT]][F][F][FLT]

Conclusion

Le système de superposition MapKit , qui offre aux développeurs iOS une incroyable flexibilité pour construire des cartes interactives et data-drivées. En combinant polylignes, polygones, cercles et tuiles avec des annotations et des reconnaissances gestuelles, vous pouvez tout créer, des applications simples basées sur la localisation aux outils de cartographie complexes. N'oubliez pas d'optimiser les performances pour les grands ensembles de données, de faire usage du cluster et de tester toujours votre comportement map , sur de vrais appareils.