97 votes

Positionnement de MKMapView pour afficher plusieurs annotations à la fois

J'ai plusieurs annotations que je veux ajouter à mon MKMapView (il peut contenir 0-n éléments, où n est généralement autour de 5). Je peux ajouter les annotations sans problème, mais je veux redimensionner la carte pour faire tenir toutes les annotations à l'écran en même temps, et je ne sais pas comment faire.

J'ai regardé -regionThatFits: mais je ne sais pas trop quoi en faire. Je vais poster un peu de code pour montrer ce que j'ai fait 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 faire ça. Si non, où serait un meilleur endroit ? -viewDidLoad ?

Merci d'avance.

138voto

Mustafa Points 8387

Le site 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 vous aidera.

- (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]; 
}

15 votes

Je pourrais t'embrasser. Ça m'a fait gagner beaucoup de temps. J'ai ajouté le code ci-dessus pour gérer un emplacement. C'est devenu un peu plus intime et personnel. Je vais poster cela comme une réponse puisque les commentaires ont tendance à mâcher le code.

0 votes

Merci beaucoup. J'ai ajouté ceci à une sous-classe de MKMapView et changé la méthode en - (void) zoomToFitAnnotations:(BOOL)animated . Fonctionne parfaitement !

1 votes

Cela fonctionne très bien. c'est aussi utile. vous pouvez changer la valeur du zoom arrière ou du zoom avant. ainsi region.span.latitudeDelta = fabs(topLeftCoord.latitude - bottomRightCoord.latitude) * 1.1 ; /// changer la valeur. quand vous augmentez la valeur : zoom arrière........ quand vous diminuez la valeur : zoom avant par exemple : region.span.latitudeDelta = fabs(topLeftCoord.latitude - bottomRightCoord.latitude) * 4.1 ;

138voto

me2 Points 1394

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);
}

6 votes

En fait, vous pouvez simplifier encore plus les choses car il n'est pas nécessaire de convertir en MKCoordinateRegion - il suffit d'appeler setVisibleMapRect : sur votre MKMapView avec le MKMapRect que vous avez créé ici.

2 votes

Les annotations sont parfois collées en haut de la carte et ne sont pas visibles. Avez-vous une idée de la meilleure façon d'augmenter le zoom après la création de la MKCoordinateRegion ?

3 votes

@KyleC [mapView setVisibleMapRect:mapRect edgePadding:UIEdgeInsetsMake(20.0f, 20.0f, 20.0f, 20.0f) animated:animated];

53voto

Code Commander Points 1352

Depuis iOS7, vous pouvez utiliser showAnnotations:animated :

[mapView showAnnotations:annotations animated:YES];

45voto

ongle Points 4567

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 pourriez étendre cette fonction en parcourant vos annotations en boucle.

Les étapes de base sont les suivantes :

  • Calculer le min lat/long

  • Calculer la lat/long max

  • Créer des objets CLLocation pour ces deux points

  • Calculer la distance entre des 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 le MapView pour l'ajuster

  • Utiliser la région ajustée pour définir la région du 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];
    }

0 votes

Cela semble être la voie à suivre. Merci.

1 votes

J'ai réussi à le faire fonctionner en utilisant MKCoordinateRegionMake : gist.github.com/1599700 au cas où quelqu'un voudrait encore le faire de cette façon.

0 votes

Region.center.latitude = (southWest.latitude + northEast.latitude) / 2.0 ; Merci pour cela

21voto

PKCLsoft Points 689

J'ai une réponse différente. J'allais implémenter l'algorithme de zoom à adapter moi-même, mais je me suis dit qu'Apple doit avoir un moyen de faire ce que nous voulions sans trop de travail. L'utilisation de la documentation API m'a rapidement montré que je pouvais utiliser MKPolygon pour faire ce dont j'avais besoin :

/* 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 vous aidera.

0 votes

Aucun problème. Il existe généralement une meilleure solution si vous êtes prêt et si vous avez le temps d'y consacrer du temps.

0 votes

Je trouve que cela place les épingles un peu trop près du bord de l'écran. Essayez d'ajouter annotationsRegion.span.latitudeDelta = annotationsRegion.span.latitudeDelta * kEventMapDetailBorderFactor ; juste avant le setRegion.

0 votes

Vous avez raison @AdamEberbach, mais il semble que votre clip inclut une constante qui n'est pas disponible. Avez-vous trouvé une valeur qui donne une "belle" bordure autour des broches ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X