Positionner MKMapView pour afficher plusieurs annotations à la fois

J’ai plusieurs annotations que je souhaite append à mon MKMapView (il pourrait s’agir de 0-n éléments, où n est généralement autour de 5). Je peux append les annotations correctement, mais je souhaite redimensionner la carte pour l’adapter à toutes les annotations à l’écran en même temps, et je ne suis pas sûr de savoir comment procéder.

J’ai regardé -regionThatFits: mais je ne sais pas trop quoi en faire. Je posterai du code pour montrer ce que j’ai eu jusqu’à présent. Je pense que cela devrait être une tâche généralement simple mais je me sens un peu dépassé par MapKit jusqu’à présent.

 - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{ location = newLocation.coordinate; //One location is obtained.. just zoom to that location MKCoordinateRegion region; region.center = location; //Set Zoom level using Span MKCoordinateSpan span; span.latitudeDelta = 0.015; span.longitudeDelta = 0.015; region.span = span; // Set the region here... but I want this to be a dynamic size // Obviously this should be set after I've added my annotations [mapView setRegion:region animated:YES]; // Test data, using these as annotations for now NSArray *arr = [NSArray arrayWithObjects:@"one", @"two", @"three", @"four", nil]; float ex = 0.01; for (NSSsortingng *s in arr) { JBAnnotation *placemark = [[JBAnnotation alloc] initWithLat:(location.latitude + ex) lon:location.longitude]; [mapView addAnnotation:placemark]; ex = ex + 0.005; } // What do I do here? [mapView setRegion:[mapView regionThatFits:region] animated:YES]; } 

Notez que tout cela se produit lorsque je reçois une mise à jour de localisation … Je ne sais pas si c’est un endroit approprié pour le faire. Si non, où serait un meilleur endroit? -viewDidLoad ?

Merci d’avance.

À partir d’iOS7, vous pouvez utiliser showAnnotations: animé:

 [mapView showAnnotations:annotations animated:YES]; 

Le lien publié par Jim est maintenant mort, mais j’ai pu trouver le code (que j’avais mis en signet quelque part). J’espère que cela t’aides.

 - (void)zoomToFitMapAnnotations:(MKMapView *)mapView { if ([mapView.annotations count] == 0) return; CLLocationCoordinate2D topLeftCoord; topLeftCoord.latitude = -90; topLeftCoord.longitude = 180; CLLocationCoordinate2D bottomRightCoord; bottomRightCoord.latitude = 90; bottomRightCoord.longitude = -180; for(id annotation in mapView.annotations) { topLeftCoord.longitude = fmin(topLeftCoord.longitude, annotation.coordinate.longitude); topLeftCoord.latitude = fmax(topLeftCoord.latitude, annotation.coordinate.latitude); bottomRightCoord.longitude = fmax(bottomRightCoord.longitude, annotation.coordinate.longitude); bottomRightCoord.latitude = fmin(bottomRightCoord.latitude, annotation.coordinate.latitude); } MKCoordinateRegion region; region.center.latitude = topLeftCoord.latitude - (topLeftCoord.latitude - bottomRightCoord.latitude) * 0.5; region.center.longitude = topLeftCoord.longitude + (bottomRightCoord.longitude - topLeftCoord.longitude) * 0.5; // Add a little extra space on the sides region.span.latitudeDelta = fabs(topLeftCoord.latitude - bottomRightCoord.latitude) * 1.1; region.span.longitudeDelta = fabs(bottomRightCoord.longitude - topLeftCoord.longitude) * 1.1; region = [mapView regionThatFits:region]; [mapView setRegion:region animated:YES]; } 

Pourquoi si compliqué?

 MKCoordinateRegion coordinateRegionForCoordinates(CLLocationCoordinate2D *coords, NSUInteger coordCount) { MKMapRect r = MKMapRectNull; for (NSUInteger i=0; i < coordCount; ++i) { MKMapPoint p = MKMapPointForCoordinate(coords[i]); r = MKMapRectUnion(r, MKMapRectMake(px, py, 0, 0)); } return MKCoordinateRegionForMapRect(r); } 

J’ai fait quelque chose de similaire pour effectuer un zoom arrière (ou un zoom arrière) sur une zone comprenant une annotation ponctuelle et l’emplacement actuel. Vous pouvez développer ceci en parcourant vos annotations.

Les étapes de base sont les suivantes:

  • Calculer le lat minimum / long
  • Calculer le maximum lat / long
  • Créer des objects CLLocation pour ces deux points
  • Calculer la distance entre les points
  • Créer une région en utilisant le point central entre les points et la distance convertie en degrés
  • Pass région dans MapView pour ajuster
  • Utiliser la région ajustée pour définir la région MapView
  -(IBAction)zoomOut:(id)sender { CLLocationCoordinate2D southWest = _newLocation.coordinate; CLLocationCoordinate2D northEast = southWest; southWest.latitude = MIN(southWest.latitude, _annotation.coordinate.latitude); southWest.longitude = MIN(southWest.longitude, _annotation.coordinate.longitude); northEast.latitude = MAX(northEast.latitude, _annotation.coordinate.latitude); northEast.longitude = MAX(northEast.longitude, _annotation.coordinate.longitude); CLLocation *locSouthWest = [[CLLocation alloc] initWithLatitude:southWest.latitude longitude:southWest.longitude]; CLLocation *locNorthEast = [[CLLocation alloc] initWithLatitude:northEast.latitude longitude:northEast.longitude]; // This is a diag distance (if you wanted tighter you could do NE-NW or NE-SE) CLLocationDistance meters = [locSouthWest getDistanceFrom:locNorthEast]; MKCoordinateRegion region; region.center.latitude = (southWest.latitude + northEast.latitude) / 2.0; region.center.longitude = (southWest.longitude + northEast.longitude) / 2.0; region.span.latitudeDelta = meters / 111319.5; region.span.longitudeDelta = 0.0; _savedRegion = [_mapView regionThatFits:region]; [_mapView setRegion:_savedRegion animated:YES]; [locSouthWest release]; [locNorthEast release]; } 

J’ai une réponse différente. J’allais implémenter moi-même l’algorithme de zoom-to-fit, mais je pensais qu’Apple devait pouvoir faire ce que nous voulions sans trop de travail. L’utilisation du doco API a rapidement montré que je pouvais utiliser MKPolygon pour faire ce qui était nécessaire:

 /* this simply adds a single pin and zooms in on it nicely */ - (void) zoomToAnnotation:(MapAnnotation*)annotation { MKCoordinateSpan span = {0.027, 0.027}; MKCoordinateRegion region = {[annotation coordinate], span}; [mapView setRegion:region animated:YES]; } /* This returns a rectangle bounding all of the pins within the supplied array */ - (MKMapRect) getMapRectUsingAnnotations:(NSArray*)theAnnotations { MKMapPoint points[[theAnnotations count]]; for (int i = 0; i < [theAnnotations count]; i++) { MapAnnotation *annotation = [theAnnotations objectAtIndex:i]; points[i] = MKMapPointForCoordinate(annotation.coordinate); } MKPolygon *poly = [MKPolygon polygonWithPoints:points count:[theAnnotations count]]; return [poly boundingMapRect]; } /* this adds the provided annotation to the mapview object, zooming as appropriate */ - (void) addMapAnnotationToMapView:(MapAnnotation*)annotation { if ([annotations count] == 1) { // If there is only one annotation then zoom into it. [self zoomToAnnotation:annotation]; } else { // If there are several, then the default behaviour is to show all of them // MKCoordinateRegion region = MKCoordinateRegionForMapRect([self getMapRectUsingAnnotations:annotations]); if (region.span.latitudeDelta < 0.027) { region.span.latitudeDelta = 0.027; } if (region.span.longitudeDelta < 0.027) { region.span.longitudeDelta = 0.027; } [mapView setRegion:region]; } [mapView addAnnotation:annotation]; [mapView selectAnnotation:annotation animated:YES]; } 

J'espère que cela t'aides.

vous pouvez aussi le faire de cette façon.

 // Position the map so that all overlays and annotations are visible on screen. MKMapRect regionToDisplay = [self mapRectForAnnotations:annotationsToDisplay]; if (!MKMapRectIsNull(regionToDisplay)) myMapView.visibleMapRect = regionToDisplay; - (MKMapRect) mapRectForAnnotations:(NSArray*)annotationsArray { MKMapRect mapRect = MKMapRectNull; //annotations is an array with all the annotations I want to display on the map for (id annotation in annotations) { MKMapPoint annotationPoint = MKMapPointForCoordinate(annotation.coordinate); MKMapRect pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0, 0); if (MKMapRectIsNull(mapRect)) { mapRect = pointRect; } else { mapRect = MKMapRectUnion(mapRect, pointRect); } } return mapRect; } 

Sur la base des informations et des suggestions de tout le monde, j’ai trouvé ce qui suit. Merci à tous ceux qui ont participé à cette discussion 🙂 Cela irait dans la vue Controller qui contient la mapView.

 - (void)zoomToFitMapAnnotations { if ([self.mapView.annotations count] == 0) return; int i = 0; MKMapPoint points[[self.mapView.annotations count]]; //build array of annotation points for (id annotation in [self.mapView annotations]) points[i++] = MKMapPointForCoordinate(annotation.coordinate); MKPolygon *poly = [MKPolygon polygonWithPoints:points count:i]; [self.mapView setRegion:MKCoordinateRegionForMapRect([poly boundingMapRect]) animated:YES]; } 

Dans mon cas, je commence par les objects CLLocation et crée des annotations pour chacun d’eux.
Je n’ai besoin que de placer deux annotations, donc j’ai une approche simple pour construire le tableau de points, mais cela pourrait facilement être développé pour construire un tableau avec une longueur arbitraire à partir d’un ensemble de CLLocations.

Voici mon implémentation (ne nécessite pas la création de MKMapPoints):

 //start with a couple of locations CLLocation *storeLocation = store.address.location.clLocation; CLLocation *userLocation = [LBLocationController sharedController].currentLocation; //build an array of points however you want CLLocationCoordinate2D points[2] = {storeLocation.coordinate, userLocation.coordinate}; //the magic part MKPolygon *poly = [MKPolygon polygonWithCoordinates:points count:2]; [self.mapView setRegion:MKCoordinateRegionForMapRect([poly boundingMapRect])]; 

En utilisant Swift, un polygone et un remplissage supplémentaire, j’ai utilisé ce qui suit:

 func zoomToFit() { var allLocations:[CLLocationCoordinate2D] = [ CLLocationCoordinate2D(latitude: 32.768805, longitude: -117.167119), CLLocationCoordinate2D(latitude: 32.770480, longitude: -117.148385), CLLocationCoordinate2D(latitude: 32.869675, longitude: -117.212929) ] var poly:MKPolygon = MKPolygon(coordinates: &allLocations, count: allLocations.count) self.mapView.setVisibleMapRect(poly.boundingMapRect, edgePadding: UIEdgeInsetsMake(40.0, 40.0, 40.0, 40.0), animated: false) } 

Voici l’équivalent SWIFT (Confirmé Travailler dans: Xcode6.1, SDK 8.2) pour les réponses de Mustafa:

  func zoomToFitMapAnnotations() { if self.annotations.count == 0 {return} var topLeftCoordinate = CLLocationCoordinate2D(latitude: -90, longitude: 180) var bottomRightCoordinate = CLLocationCoordinate2D(latitude: 90, longitude: -180) var i = 1 for object in self.annotations { if let annotation = object as? MKAnnotation { topLeftCoordinate.longitude = fmin(topLeftCoordinate.longitude, annotation.coordinate.longitude) topLeftCoordinate.latitude = fmin(topLeftCoordinate.latitude, annotation.coordinate.latitude) bottomRightCoordinate.longitude = fmin(bottomRightCoordinate.longitude, annotation.coordinate.longitude) bottomRightCoordinate.latitude = fmin(bottomRightCoordinate.latitude, annotation.coordinate.latitude) } } var center = CLLocationCoordinate2D(latitude: topLeftCoordinate.latitude - (topLeftCoordinate.latitude - bottomRightCoordinate.latitude) * 0.5, longitude: topLeftCoordinate.longitude - (topLeftCoordinate.longitude - bottomRightCoordinate.longitude) * 0.5) print("\ncenter:\(center.latitude) \(center.longitude)") // Add a little extra space on the sides var span = MKCoordinateSpanMake(fabs(topLeftCoordinate.latitude - bottomRightCoordinate.latitude) * 1.01, fabs(bottomRightCoordinate.longitude - topLeftCoordinate.longitude) * 1.01) print("\nspan:\(span.latitudeDelta) \(span.longitudeDelta)") var region = MKCoordinateRegion(center: center, span: span) region = self.regionThatFits(region) self.setRegion(region, animated: true) } 

Il y a une nouvelle méthode dans ‘MKMapView’ à partir d’iOS 7 que vous pouvez utiliser

Déclaration

RAPIDE

 func showAnnotations(_ annotations: [AnyObject]!, animated animated: Bool) 

OBJECTIF C

 - (void)showAnnotations:(NSArray *)annotations animated:(BOOL)animated 

Paramètres

annotations Les annotations que vous souhaitez voir visibles sur la carte. animée OUI si vous souhaitez que la région de la carte soit animée ou NON si vous souhaitez que la carte affiche immédiatement la nouvelle région sans animations.

Discussion

L’appel de cette méthode met à jour la valeur dans la propriété region et éventuellement d’autres propriétés pour refléter la nouvelle région de map.

Une solution possible pourrait être de mesurer la distance entre l’emplacement actuel et toutes les annotations et d’utiliser la méthode MKCoordinateRegionMakeWithDistance pour créer une région dont la distance est légèrement supérieure à celle de l’annotation la plus éloignée.

Bien sûr, cela ralentirait le nombre d’annotations ajoutées.

 - (void)zoomToFitMapAnnotations { if ([self.mapview.annotations count] == 0) return; int i = 0; MKMapPoint points[[self.mapview.annotations count]]; //build array of annotation points for (id annotation in [self.mapview annotations]) points[i++] = MKMapPointForCoordinate(annotation.coordinate); MKPolygon *poly = [MKPolygon polygonWithPoints:points count:i]; [self.mapview setRegion:MKCoordinateRegionForMapRect([poly boundingMapRect]) animated:YES]; } 

Basé sur l’excellente réponse de me2 (maintenant dans Swift)

 func coordinateRegionForCoordinates(coords: [CLLocationCoordinate2D]) -> MKCoordinateRegion { var rect: MKMapRect = MKMapRectNull for coord in coords { let point: MKMapPoint = MKMapPointForCoordinate(coord) rect = MKMapRectUnion(rect, MKMapRectMake(point.x, point.y, 0, 0)) } return MKCoordinateRegionForMapRect(rect) } 

Ajout d’une petite clause if pour gérer 1 emplacement – pour append l’extrait de code Cound de mustufa. Utilisé la fonction zoomToAnnotation de pkclSoft pour cela:

 if ([mapView.annotations count] == 1){ MKCoordinateSpan span = {0.027, 0.027}; region.span = span; CLLocationCoordinate2D singleCoordinate = [[mapView.annotations objectAtIndex:0] coordinate]; region.center.latitude = singleCoordinate.latitude; region.center.longitude = singleCoordinate.longitude; } else { // mustufa's code } 

Je sais que c’est une vieille question mais, si vous voulez afficher toutes les annotations déjà sur la carte, utilisez ceci:

  mapView.showAnnotations(mapView.annotations, animated: true) 

J’espère que c’est au moins pertinent, c’est ce que j’ai mis en place pour Mono (basé sur la réponse de pkclSoft):

 void ZoomMap (MKMapView map) { var annotations = map.Annotations; if (annotations == null || annotations.Length == 0) return; var points = annotations.OfType () .Select (s => MKMapPoint.FromCoordinate (s.Coordinate)) .ToArray (); map.SetVisibleMapRect(MKPolygon.FromPoints (points).BoundingMapRect, true); } 
 CLLocationCoordinate2D min = CLLocationCoordinate2DMake(99999.0, 99999.0); CLLocationCoordinate2D max = CLLocationCoordinate2DMake(-99999.0, -99999.0); // find max/min.... // zoom to cover area // TODO: Maybe better using a MKPolygon which can calculate its own fitting region. CLLocationCoordinate2D center = CLLocationCoordinate2DMake((max.latitude + min.latitude) / 2.0, (max.longitude + min.longitude) / 2.0); MKCoordinateSpan span = MKCoordinateSpanMake(max.latitude - min.latitude, max.longitude - min.longitude); MKCoordinateRegion region = MKCoordinateRegionMake(center, span); [_mapView setRegion:[_mapView regionThatFits:region] animated:YES]; 

Sur la base de la réponse à me2, j’ai écrit une catégorie pour MKMapView pour append des marges et ignorer les annotations d’emplacement de l’utilisateur:

 @interface MKMapView (ZoomToFitAnnotations) - (void)zoomToFitAnnotations:(BOOL)animated; @end @implementation MKMapView (ZoomToFitAnnotations) - (void)zoomToFitAnnotations:(BOOL)animated { if (self.annotations.count == 0) return; MKMapRect rect = MKMapRectNull; for (id annotation in self.annotations) { if ([annotation isKindOfClass:[MKUserLocation class]] == false) { MKMapPoint point = MKMapPointForCoordinate(annotation.coordinate); rect = MKMapRectUnion(rect, MKMapRectMake(point.x, point.y, 0, 0)); } } MKCoordinateRegion region = MKCoordinateRegionForMapRect(rect); region.span.longitudeDelta *= 2; // Margin region.span.latitudeDelta *= 2; // Margin [self setRegion:region animated:animated]; } @end 

Comme je ne peux pas commenter une réponse, j’aimerais append un peu de commodité à la réponse de @ me2 (puisque je pensais que c’était l’approche la plus élégante trouvée ici).

Pour mon projet personnel, j’ai simplement ajouté une catégorie sur la classe MKMapView pour encapsuler la fonctionnalité “zone visible” pour une opération très courante: pour pouvoir voir toutes les annotations actuellement chargées sur l’instance de MKMapView. le résultat était le suivant:

Fichier .h

 #import  @interface MKMapView (Extensions) -(void)ij_setVisibleRectToFitAllLoadedAnnotationsAnimated:(BOOL)animated; -(void)ij_setVisibleRectToFitAnnotations:(NSArray *)annotations animated:(BOOL)animated; @end 

fichier .m

 #import "MKMapView+Extensions.h" @implementation MKMapView (Extensions) /** * Changes the currently visible portion of the map to a region that best fits all the currently loadded annotations on the map, and it optionally animates the change. * * @param animated is the change should be perfomed with an animation. */ -(void)ij_setVisibleRectToFitAllLoadedAnnotationsAnimated:(BOOL)animated { MKMapView * mapView = self; NSArray * annotations = mapView.annotations; [self ij_setVisibleRectToFitAnnotations:annotations animated:animated]; } /** * Changes the currently visible portion of the map to a region that best fits the provided annotations array, and it optionally animates the change. All elements from the array must conform to the  protocol in order to fetch the coordinates to compute the visible region of the map. * * @param annotations an array of elements conforming to the  protocol, holding the locations for which the visible portion of the map will be set. * @param animated wether or not the change should be perfomed with an animation. */ -(void)ij_setVisibleRectToFitAnnotations:(NSArray *)annotations animated:(BOOL)animated { MKMapView * mapView = self; MKMapRect r = MKMapRectNull; for (id a in annotations) { ZAssert([a conformsToProtocol:@protocol(MKAnnotation)], @"ERROR: All elements of the array MUST conform to the MKAnnotation protocol. Element (%@) did not fulfill this requirement", a); MKMapPoint p = MKMapPointForCoordinate(a.coordinate); //MKMapRectUnion performs the union between 2 rects, returning a bigger rect containing both (or just one if the other is null). here we do it for rects without a size (points) r = MKMapRectUnion(r, MKMapRectMake(px, py, 0, 0)); } [mapView setVisibleMapRect:r animated:animated]; } @end 

Comme vous pouvez le voir, j’ai ajouté 2 méthodes jusqu’ici: une pour définir la région visible de la carte sur celle qui correspond à toutes les annotations actuellement chargées sur l’instance MKMapView et une autre méthode pour la définir sur n’importe quel tableau d’objects. Donc, pour définir la région visible de la mapView, le code serait aussi simple que:

  //the mapView instance [self.mapView ij_setVisibleRectToFitAllLoadedAnnotationsAnimated:animated]; 

J’espère que ça aide =)

ce code fonctionne pour moi, il montre toutes les broches avec l’emplacement actuel, j’espère que cela vous aide,

 func setCenterForMap() { var mapRect: MKMapRect = MKMapRectNull for loc in mapView.annotations { let point: MKMapPoint = MKMapPointForCoordinate(loc.coordinate) print( "location is : \(loc.coordinate)"); mapRect = MKMapRectUnion(mapRect, MKMapRectMake(point.x,point.y,0,0)) } if (locationManager.location != nil) { let point: MKMapPoint = MKMapPointForCoordinate(locationManager.location!.coordinate) print( "Cur location is : \(locationManager.location!.coordinate)"); mapRect = MKMapRectUnion(mapRect, MKMapRectMake(point.x,point.y,0,0)) } mapView.setVisibleMapRect(mapRect, edgePadding: UIEdgeInsetsMake(40.0, 40.0, 40.0, 40.0), animated: true) } 

Considérez cette extension:

 extension MKCoordinateRegion { init(locations: [CLLocationCoordinate2D], marginMultiplier: Double = 1.1) { let mapRect = locations.reduce(MKMapRect(), { let point = MKMapPointForCoordinate($1) let rect = MKMapRect(origin: point, size: MKMapSize(width: 0.0, height: 0.0)) return MKMapRectUnion($0, rect) }) var coordinateRegion = MKCoordinateRegionForMapRect(mapRect) coordinateRegion.span.latitudeDelta *= marginMultiplier coordinateRegion.span.longitudeDelta *= marginMultiplier self = coordinateRegion } }