Positionnement de MKMapView pour afficher plusieurs annotations à la fois

93

J'ai plusieurs annotations que je veux ajouter à mon MKMapView (il pourrait 0-n éléments, où n est généralement autour de 5). Je peux bien ajouter les annotations, mais je souhaite redimensionner la carte pour qu'elle corresponde à toutes les annotations à l'écran à la fois, et je ne sais pas comment procéder.

J'ai regardé -regionThatFits:mais je ne sais pas trop quoi en faire. Je publierai du code pour montrer ce que j'ai 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 (NSString *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];
}

Remarquez, tout cela se produit lorsque je reçois une mise à jour de l'emplacement ... Je ne sais pas si c'est un endroit approprié pour le faire. Sinon, quel serait le meilleur endroit? -viewDidLoad?

Merci d'avance.

jbrennan
la source

Réponses:

137

Le lien posté 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<MKAnnotation> 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]; 
}
Mustafa
la source
14
Je pourrais t'embrasser. Cela m'a fait gagner du temps. J'ai ajouté le code ci-dessus pour gérer 1 emplacement. Il est devenu un peu plus proche et personnel. Je posterai cela comme réponse car les commentaires ont tendance à mâcher le code.
Michael Reed
Merci beaucoup. J'ai ajouté ceci à une sous-classe de MKMapViewet changé la méthode en - (void) zoomToFitAnnotations:(BOOL)animated. Fonctionne parfaitement!
simonbs
1
ça marche très bien. aussi c'est utile. vous pouvez modifier la valeur de zoom arrière ou de zoom avant. donc region.span.latitudeDelta = fabs (topLeftCoord.latitude - bottomRightCoord.latitude) * 1.1; /// changer la valeur. lorsque vous augmentez la valeur: zoom arrière ........ lorsque vous diminuez la valeur: zoomez par exemple: region.span.latitudeDelta = fabs (topLeftCoord.latitude - bottomRightCoord.latitude) * 4.1;
Erhan Demirci
1
@ MR.Mustafa: Son fonctionnement, génial! Mais je ne pense pas que résoudre le problème est suffisant. Alors s'il vous plaît, quelqu'un m'explique comment cela fonctionne. Ou via des liens. Désolé si je suis idiot, je suis un débutant. Support de Pls. Merci
Siddarth Hailstorm
1
@Mustafa ... Merci, ça m'a sauvé la journée.
Vvk du
133

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(p.x, p.y, 0, 0));
    }
    return MKCoordinateRegionForMapRect(r);
}
moi2
la source
6
incroyable à quel point c'est plus simple, plus propre et plus facile que les alternatives affichées. en fait, vous pouvez encore simplifier cela car il n'est pas nécessaire de convertir en MKCoordinateRegion - appelez simplement setVisibleMapRect: sur votre MKMapView avec le MKMapRect que vous créez ici.
lensovet
2
Les annotations sont parfois collées en haut de la carte et ne sont pas visibles. Une entrée sur la meilleure approche pour augmenter le zoom après la création de MKCoordinateRegion?
Kyle C
3
@KyleC[mapView setVisibleMapRect:mapRect edgePadding:UIEdgeInsetsMake(20.0f, 20.0f, 20.0f, 20.0f) animated:animated];
utilisateur
Comment créez-vous le CLLocationCoordinate2D *coordstableau? Utilisation malloc()?
Hlung
3
@KyleC. J'ai ajouté ceci avant de revenir, rce qui CGFloat zoomOutPercent = 0.2f; r = MKMapRectMake(r.origin.x-r.size.width*zoomOutPercent, r.origin.y-r.size.height*zoomOutPercent, r.size.width*(1+zoomOutPercent*2), r.size.height*(1+zoomOutPercent*2));
réduit
44

J'ai fait quelque chose de similaire pour effectuer un zoom arrière (ou avant) sur une zone qui comprenait une annotation de point et l'emplacement actuel. Vous pouvez développer cela en parcourant vos annotations.

Les étapes de base sont:

  • Calculez le min lat / long
  • Calculez le max lat / long
  • Créer des objets 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
  • Passer la région dans MapView pour l'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];
    }
Donald Byrd
la source
Cela ressemble à la voie à suivre. Merci!
jbrennan
1
Géré pour que cela fonctionne en utilisant MKCoordinateRegionMake: gist.github.com/1599700 au cas où quelqu'un voudrait toujours le faire de cette façon.
chakrit
region.center.latitude = (sud-ouest.latitude + nord-est.latitude) / 2.0; Merci pour cela
Tony
Cela fonctionne-t-il avec des points de chaque côté du méridien? L'Équateur?
Eliot
1
Ce code place les emplacements hors écran lorsque les emplacements ont une valeur y similaire. Par exemple, montrer deux emplacements à (50, -4) et (100, -3) agrandira la carte trop loin, plaçant les coordonnées à gauche et à droite de l'écran.
utilisateur
21

J'ai une réponse différente. J'allais implémenter moi-même l'algorithme du zoom pour ajuster, mais j'ai pensé qu'Apple devait avoir un moyen de faire ce que nous voulions sans trop de travail. L'utilisation de l'API doco 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.

PKCLsoft
la source
Pas de problème. Il existe généralement un meilleur moyen si vous le souhaitez et que vous avez le temps d'y consacrer.
PKCLsoft le
J'ai trouvé que cela place les broches un peu trop près du bord de l'écran. Essayez d'ajouter annotationsRegion.span.latitudeDelta = annotationsRegion.span.latitudeDelta * kEventMapDetailBorderFactor; juste avant le setRegion.
Adam Eberbach
Vous avez raison @AdamEberbach, mais il semble que votre clip inclut une constante qui n'est pas disponible. Avez-vous trouvé une valeur qui offrait une «belle» bordure autour des broches?
PKCLsoft
La réponse de Code Commander ci-dessous à propos de l'utilisation de la nouvelle méthode showAnnotations avec iOS7 ajoute une belle marge, qui fonctionne mieux, bien que ce code soit plus cool.
James Toomey
14

vous pouvez également 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<MKAnnotation> 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;
}
Manish Ahuja
la source
13

Sur la base des informations et des suggestions de tout le monde, j'ai trouvé ce qui suit. Merci à tous ceux qui participent à cette discussion pour leur contribution :) 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<MKAnnotation> 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]; 
}
nh32rg
la source
Cela devrait susciter davantage de votes. Très précis et au point.
Natasha
5

Dans mon cas, je commence par les objets CLLocation et je 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 il pourrait facilement être étendu pour créer un tableau avec une longueur arbitraire étant donné 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])];
Jacobsimeon
la source
5

En utilisant Swift, un polygone et un rembourrage 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)
}

Lindsay Thurmond
la source
setVisibleMapRect (...). Je faisais le calcul moi-même ... mal.
CodeReaper
3

Il existe 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 apparaître sur la carte. animé OUI si vous voulez que le changement de région de la carte soit animé, ou NON si vous voulez que la carte affiche la nouvelle région immédiatement sans animations.

Discussion

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

Mat
la source
3

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)
Paul Lehn
la source
3

Voici l'équivalent SWIFT (travail confirmé 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)

    for object in self.annotations {
        if let annotation = object as? MKAnnotation {
            topLeftCoordinate.longitude = fmin(topLeftCoordinate.longitude, annotation.coordinate.longitude)
            topLeftCoordinate.latitude = fmax(topLeftCoordinate.latitude, annotation.coordinate.latitude)
            bottomRightCoordinate.longitude = fmax(bottomRightCoordinate.longitude, annotation.coordinate.longitude)
            bottomRightCoordinate.latitude = fmin(bottomRightCoordinate.latitude, annotation.coordinate.latitude)
        }
    }

    let 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
    let 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)

}
Saru
la source
1
Salut iOS_Developer. Merci pour la conversion Swift. Pour moi, cela ne fonctionne pas parce que je pense qu'il vous manque deux "fmax" au lieu de "fmin" pour topLeftCoordinate.latitude et bottomRightCoordinate.longitude.
Philipp Otto
2

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 à l'annotation la plus éloignée.

Cela deviendrait bien sûr plus lent avec le nombre d'annotations ajoutées.

criscokid
la source
Je parcourais la section des commentaires uniquement pour me valider. Heureux que quelqu'un d'autre pense comme moi :-) Puisque je n'ajoute que deux annotations (début et fin), je n'ai ressenti aucune lenteur.
thandasoru
2
- (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<MKAnnotation> 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];
}
user3042729
la source
2

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)
}
tilo
la source
1

Ajout d'une petite clause if pour gérer 1 emplacement - à ajouter à l'extrait de code cound de mustufa. Utilisation de 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
}
Michael Reed
la source
1

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

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)

}
Patel Jigar
la source
0

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<MapAnnotation> ()
                            .Select (s => MKMapPoint.FromCoordinate (s.Coordinate))
                            .ToArray ();            

    map.SetVisibleMapRect(MKPolygon.FromPoints (points).BoundingMapRect, true); 
}
nullable
la source
0
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];
VSN
la source
0

Sur la base de la réponse me2, j'ai écrit une catégorie pour MKMapView pour ajouter des marges et ignorer l'annotation de l'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<MKAnnotation> 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
Tomasz
la source
0

Comme je ne peux pas commenter une réponse, j'aimerais ajouter mon 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: paramétrer pour pouvoir voir toutes les annotations actuellement chargées sur l'instance MKMapView. le résultat était le suivant:

fichier .h

#import <MapKit/MapKit.h>

@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 <MKAnnotation> protocol in order to fetch the coordinates to compute the visible region of the map.
 *
 *  @param annotations an array of elements conforming to the <MKAnnotation> 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<MKAnnotation> 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(p.x, p.y, 0, 0));
    }

    [mapView setVisibleMapRect:r animated:animated];

}

@end

Comme vous pouvez le voir, j'ai ajouté jusqu'à présent 2 méthodes: 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'objets. Donc, pour définir la région visible de mapView, le code serait alors aussi simple que:

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

J'espère que ça aide =)

Robertibiris
la source
0

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
    }
}
nsmeme
la source
0

Une version rapide 5:

   func regionFor(coordinates coords: [CLLocationCoordinate2D]) -> MKCoordinateRegion {
        var r = MKMapRect.null

        for i in 0 ..< coords.count {
            let p = MKMapPoint(coords[i])

            r = r.union(MKMapRect(x: p.x, y: p.y, width: 0, height: 0))
        }

        return MKCoordinateRegion(r)
    }
Stéphane de Luca
la source
0

Ajout à la réponse de Stéphane de Luca . Ici, nous pouvons conserver UserLocation plus les annotations personnalisées pour s'adapter à MKMapView

 private func centerViewOnUserLocation() {
    
    if selectedLatitudeFromPreviousVC?.description != nil && selectedLongitudeFromPreviousVC?.description != nil {
        
        if let location = locationManager.location?.coordinate {
            let region = regionFor(coordinates: [
                CLLocationCoordinate2D(latitude: selectedLatitudeFromPreviousVC!, longitude: selectedLongitudeFromPreviousVC!),
                location])
            mkmapView.setRegion(region, animated: true)
        }
    } else {
        if let location = locationManager.location?.coordinate {
            let region = MKCoordinateRegion.init(center: location, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
            mkmapView.setRegion(region, animated: true)
        }
    }
}

private func regionFor(coordinates coords: [CLLocationCoordinate2D]) -> MKCoordinateRegion {
    var r = MKMapRect.null

    for i in 0 ..< coords.count {
        let p = MKMapPoint(coords[i])
        r = r.union(MKMapRect(x: p.x, y: p.y, width: 0, height: 0))
    }

    var coordinateRegion = MKCoordinateRegion(r)
    coordinateRegion.span.latitudeDelta *= 1.5
    coordinateRegion.span.longitudeDelta *= 1.5
    return coordinateRegion
}

entrez la description de l'image ici

Sreekanth G
la source