Zoom MKMapView pour s'adapter aux broches d'annotation?

193

J'utilise MKMapView et j'ai ajouté un certain nombre de broches d'annotation à la carte sur une zone de 5 à 10 kilomètres. Lorsque j'exécute l'application, ma carte commence à faire un zoom arrière pour montrer le monde entier, quelle est la meilleure façon de zoomer sur la carte pour que les épingles correspondent à la vue?

EDIT: Ma pensée initiale serait d'utiliser MKCoordinateRegionMake et de calculer le centre de coordonnées, longitudeDelta et latitudeDelta à partir de mes annotations. Je suis presque sûr que cela fonctionnera, mais je voulais juste vérifier que je ne manquais de rien d'évident.

Code ajouté, BTW: FGLocation est une classe conforme à MKAnnotation, locationFake est l'un NSMutableArrayde ces objets. Les commentaires sont toujours les bienvenus ....

- (MKCoordinateRegion)regionFromLocations {
    CLLocationCoordinate2D upper = [[locationFake objectAtIndex:0] coordinate];
    CLLocationCoordinate2D lower = [[locationFake objectAtIndex:0] coordinate];

    // FIND LIMITS
    for(FGLocation *eachLocation in locationFake) {
        if([eachLocation coordinate].latitude > upper.latitude) upper.latitude = [eachLocation coordinate].latitude;
        if([eachLocation coordinate].latitude < lower.latitude) lower.latitude = [eachLocation coordinate].latitude;
        if([eachLocation coordinate].longitude > upper.longitude) upper.longitude = [eachLocation coordinate].longitude;
        if([eachLocation coordinate].longitude < lower.longitude) lower.longitude = [eachLocation coordinate].longitude;
    }

    // FIND REGION
    MKCoordinateSpan locationSpan;
    locationSpan.latitudeDelta = upper.latitude - lower.latitude;
    locationSpan.longitudeDelta = upper.longitude - lower.longitude;
    CLLocationCoordinate2D locationCenter;
    locationCenter.latitude = (upper.latitude + lower.latitude) / 2;
    locationCenter.longitude = (upper.longitude + lower.longitude) / 2;

    MKCoordinateRegion region = MKCoordinateRegionMake(locationCenter, locationSpan);
    return region;
}
fuzzygoat
la source
10
Remarque sur iOS 7: La nouvelle méthode showAnnotations: animated: peut vous aider à éviter ce calcul manuel de région.

Réponses:

123

Vous avez raison.

Trouvez vos latitudes et longitudes maximales et minimales, appliquez une arithmétique simple et utilisez MKCoordinateRegionMake.

Pour iOS 7 et supérieur, utilisez à showAnnotations:animated:partir de MKMapView.h:

// Position the map such that the provided array of annotations are all visible to the fullest extent possible. 
- (void)showAnnotations:(NSArray *)annotations animated:(BOOL)animated NS_AVAILABLE(10_9, 7_0);
Matthew Frederick
la source
158
Pour iOS 7 et supérieur (référence MKMapView.h): // Position the map such that the provided array of annotations are all visible to the fullest extent possible. - (void)showAnnotations:(NSArray *)annotations animated:(BOOL)animated NS_AVAILABLE(10_9, 7_0);
Abhishek Bedi
1
Cela fonctionne bien mais parfois lorsque je fais un zoom avant (sur la carte) puis j'essaie de le centrer (en utilisant un bouton qui invoque cette méthode), cela ne semble pas fonctionner.
RPM
5
Il est important de noter que cela showAnnotationsajoute également les annotations à la carte, même si une annotation pour cet emplacement existe déjà.
Eneko Alonso
@EnekoAlonso Vous pouvez contourner ce problème en appelant removeAnnotations(_ annotations:)immédiatement aprèsshowAnnotations(_ annotations:animated)
Alain Stulz
1
Il convient également de noter que si showAnnotations définit la région pour afficher les annotations, la région est toujours ajustée pour correspondre au rapport d'aspect; et cela exclura fréquemment certaines des annotations. Notez également que showAnnotations est la seule solution correcte présentée ici; aucune des autres réponses ne tente même de gérer les annotations couvrant la ligne de date internationale.
Gordon Dove
335

C'est celui que j'ai trouvé ici qui a fonctionné pour moi:

(EDIT: J'ai mis à jour la solution en utilisant la suggestion de @ Micah d'augmenter le pointRect de 0,1 pour garantir que le rect ne finit pas par être infinitésimalement petit!)

MKMapRect zoomRect = MKMapRectNull;
for (id <MKAnnotation> annotation in mapView.annotations)
{
    MKMapPoint annotationPoint = MKMapPointForCoordinate(annotation.coordinate);
    MKMapRect pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0.1, 0.1);
    zoomRect = MKMapRectUnion(zoomRect, pointRect);
}
[mapView setVisibleMapRect:zoomRect animated:YES];

 

Vous pouvez également le mettre à jour pour inclure la broche userLocation en remplaçant la première ligne par:

MKMapPoint annotationPoint = MKMapPointForCoordinate(mapView.userLocation.coordinate);
MKMapRect zoomRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0.1, 0.1);
jowie
la source
4
Bien en effet. Vous n'avez cependant pas besoin de vérifier isNull. MKMapRectUnion fait cela pour vous. De la documentation: "Si l'un des rectangle est nul, cette méthode renvoie l'autre rectangle."
Felix Lamouroux du
37
Très belle solution !!! Voici une petite touche supplémentaire pour ajouter du rembourrage: double inset = -zoomRect.size.width * 0.1; [self.mapView setVisibleMapRect: MKMapRectInset (zoomRect, encart, encart) animé: OUI];
Craig B
1
Impressionnant! Ajout potentiel: si vous souhaitez exclure «l'annotation de l'emplacement actuel», ajoutez simplement une instruction if dans la boucle for: if (! [Annotation isKindOfClass: [MKUserLocation class]]) {// Faites des choses ici}
kgaidis
2
La solution @CraigB pour le rembourrage est excellente, mais elle ne fonctionne pas bien lorsque le chemin est vertical, par exemple un mouvement du sud vers le nord, pour résoudre ce problème, utilisez un double insert = MIN (-zoomRect.size.width * 0.1, -zoomRect.size. hauteur * 0,1);
Farhad Malekpour
1
Amélioration avec rembourrage: double insetWidth = -zoomRect.size.width * 0,2; double insetHeight = -zoomRect.size.height * 0,2; MKMapRect insetRect = MKMapRectInset (zoomRect, insetWidth, insetHeight); Ensuite, utilisez ce nouvel insertRect
dulgan
121

Apple a ajouté une nouvelle méthode pour IOS 7 pour simplifier un peu la vie.

[mapView showAnnotations:yourAnnotationArray animated:YES];

Vous pouvez facilement extraire un tableau stocké dans la vue de la carte:

yourAnnotationArray = mapView.annotations;

et ajustez rapidement la caméra aussi!

mapView.camera.altitude *= 1.4;

cela ne fonctionnera que si l'utilisateur a installé iOS 7+ ou OS X 10.9+. découvrez l'animation personnalisée ici

Ryan Berg
la source
Je ne suis pas sûr que ce soit à cause d'autres facteurs dans mon implémentation, mais je trouve que showAnnotationscela ne fait pas un zoom / ajustement des annotations aussi proche que l'implémentation manuelle, donc je suis resté avec celui manuel.
Ted Avery
1
essayez de multiplier l'altitude des caméras par une fraction d'un, comme mapView.camera.altitude * = .85; pour une vue rapprochée
Ryan Berg
J'ai également trouvé cela utile pour sélectionner des annotations en dehors de la zone de carte visible actuelle. Par défaut, MapView ne sélectionne pas les annotations non visibles. Appelez showAnnotations avec un tableau de vos annotations non visibles, avant d'appeler selectAnnotation, et la carte devrait mettre à jour sa zone visible.
MandisaW
42

J'utilise ce code et fonctionne bien pour moi:

-(void)zoomToFitMapAnnotations:(MKMapView*)aMapView
{
    if([aMapView.annotations count] == 0)
        return;

    CLLocationCoordinate2D topLeftCoord;
    topLeftCoord.latitude = -90;
    topLeftCoord.longitude = 180;

    CLLocationCoordinate2D bottomRightCoord;
    bottomRightCoord.latitude = 90;
    bottomRightCoord.longitude = -180;

    for(MapViewAnnotation *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;
    region.span.latitudeDelta = fabs(topLeftCoord.latitude - bottomRightCoord.latitude) * 1.1; // Add a little extra space on the sides
    region.span.longitudeDelta = fabs(bottomRightCoord.longitude - topLeftCoord.longitude) * 1.1; // Add a little extra space on the sides

    region = [aMapView regionThatFits:region];
    [mapView setRegion:region animated:YES];
}
Rafael Moreira
la source
Ne fonctionne pas pour: ▿ 2 éléments ▿ 0: CLLocationCoordinate2D - latitude: 46.969995730376894 - longitude: -109.2494943434474 ▿ 1: CLLocationCoordinate2D - latitude: 63.23212154333072 - longitude: 174.13666611126533
Olexiy Pyvovarov 01
23

En utilisation rapide

mapView.showAnnotations(annotationArray, animated: true)

Dans l'objectif c

[mapView showAnnotations:annotationArray animated:YES];
Sreedeepkesav MS
la source
2
Si les annotations ont déjà été définies sur la carte, vous pouvez les référencer directement avec:mapView.showAnnotations(mapView.annotations, animated: true)
Justin Vallely
14

J'ai converti la réponse de Rafael Moreira. Le mérite lui revient. Pour ceux d'entre vous qui recherchent la version Swift, voici le code:

 func zoomToFitMapAnnotations(aMapView: MKMapView) {
    guard aMapView.annotations.count > 0 else {
        return
    }
    var topLeftCoord: CLLocationCoordinate2D = CLLocationCoordinate2D()
    topLeftCoord.latitude = -90
    topLeftCoord.longitude = 180
    var bottomRightCoord: CLLocationCoordinate2D = CLLocationCoordinate2D()
    bottomRightCoord.latitude = 90
    bottomRightCoord.longitude = -180
    for annotation: MKAnnotation in myMap.annotations as! [MKAnnotation]{
        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)
    }

    var region: MKCoordinateRegion = MKCoordinateRegion()
    region.center.latitude = topLeftCoord.latitude - (topLeftCoord.latitude - bottomRightCoord.latitude) * 0.5
    region.center.longitude = topLeftCoord.longitude + (bottomRightCoord.longitude - topLeftCoord.longitude) * 0.5
    region.span.latitudeDelta = fabs(topLeftCoord.latitude - bottomRightCoord.latitude) * 1.4
    region.span.longitudeDelta = fabs(bottomRightCoord.longitude - topLeftCoord.longitude) * 1.4
    region = aMapView.regionThatFits(region)
    myMap.setRegion(region, animated: true)
}
Prashant Khanal
la source
14

Swift 3 Ceci est de manière correcte pour ajuster toutes les annotations dans la carte.

func zoomMapaFitAnnotations() {

        var zoomRect = MKMapRectNull
        for annotation in mapview.annotations {

            let annotationPoint = MKMapPointForCoordinate(annotation.coordinate)

            let pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0, 0)

            if (MKMapRectIsNull(zoomRect)) {
                zoomRect = pointRect
            } else {
                zoomRect = MKMapRectUnion(zoomRect, pointRect)
            }
        }
        self.mapview.setVisibleMapRect(zoomRect, edgePadding: UIEdgeInsetsMake(50, 50, 50, 50), animated: true)

    }
oscar castellon
la source
@ArshadShaik La modification que vous avez suggérée a été rejetée, si vous souhaitez fournir une nouvelle réponse pour Swift 4.2, n'hésitez pas, mais ajoutez-la en tant que réponse, pas en la modifiant dans une autre publication des utilisateurs.
Nick
13

La solution de @ jowie fonctionne très bien. Une prise, si une carte n'a qu'une seule annotation, vous vous retrouverez avec une carte entièrement dézoomée. J'ai ajouté 0,1 à la taille de la marque rect pour m'assurer que setVisibleMapRect a quelque chose à zoomer.

MKMapRect pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0.1, 0.1);
Michée
la source
12

Si vous recherchez iOS 8 et supérieur , la façon la plus simple de le faire est de définir la vue var layoutMargins: UIEdgeInsets { get set }de votre carte avant d'appelerfunc showAnnotations(annotations: [MKAnnotation], animated: Bool)

Par exemple (Swift 2.1):

@IBOutlet weak var map: MKMapView! {
    didSet {
        map.delegate = self
        map.mapType = .Standard
        map.pitchEnabled = false
        map.rotateEnabled = false
        map.scrollEnabled = true
        map.zoomEnabled = true
    }
}

// call 'updateView()' when viewWillAppear or whenever you set the map annotations
func updateView() {
    map.layoutMargins = UIEdgeInsets(top: 25, left: 25, bottom: 25, right: 25)
    map.showAnnotations(map.annotations, animated: true)
}
Henrik Perrochon
la source
12

J'ai créé une extension pour afficher toutes les annotations en utilisant du code d'ici et là dans swift. Cela n'affichera pas toutes les annotations si elles ne peuvent pas être affichées même au niveau de zoom maximal.

import MapKit

extension MKMapView {
    func fitAllAnnotations() {
        var zoomRect = MKMapRectNull;
        for annotation in annotations {
            let annotationPoint = MKMapPointForCoordinate(annotation.coordinate)
            let pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0.1, 0.1);
            zoomRect = MKMapRectUnion(zoomRect, pointRect);
        }
        setVisibleMapRect(zoomRect, edgePadding: UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50), animated: true)
    }
}
Ankit Srivastava
la source
J'ai réussi à obtenir de meilleurs résultats en modifiant les UIEdgeInsetsMakeparamètres, les valeurs entre 30 et 100 étaient bonnes pour moi. Je testais en utilisant l'iPhone SE i) S 10.2 Simulator. Exemple de code: setVisibleMapRect(zoomRect, edgePadding: UIEdgeInsetsMake(100, 100, 100, 100), animated: true). Remarque: ce code fonctionne dans Swift 3 et XCode 8.2.1.
nyxee
8

Ajout de cette boucle If dans la boucle for pour exclure la broche de localisation des utilisateurs de cette méthode (obligatoire dans mon cas, et peut-être d'autres)

if (![annotation isKindOfClass:[MKUserLocation class]] ) {

//Code Here...

}
Sammio2
la source
8

Pour iOS 7 et supérieur (référence MKMapView.h):

// Position the map such that the provided array of annotations are all visible to the fullest extent possible.          

- (void)showAnnotations:(NSArray *)annotations animated:(BOOL)animated NS_AVAILABLE(10_9, 7_0);

remarque de - Abhishek Bedi

Vous appelez simplement:

 [yourMapView showAnnotations:@[yourAnnotation] animated:YES];
Seke1412
la source
Juste pour référence, le texte NS_AVAILABLE était là parce qu'en janvier 2011, avoir iOS 7 sur un appareil n'était pas très probable, et le NS_AVAILABLE protégeait l'application contre l'échec de la construction ou le crash.
Matthew Frederick
5

Dans Swift

    var zoomRect = MKMapRectNull;

    for i in 0..<self.map.annotations.count {

        let annotation: MKAnnotation = self.map.annotations[i]

        let annotationPoint = MKMapPointForCoordinate(annotation.coordinate);
        let pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0.1, 0.1);
        zoomRect = MKMapRectUnion(zoomRect, pointRect);
    }

    self.map.setVisibleMapRect(zoomRect, animated: true)
Matheus Domingos
la source
5
    var zoomRect: MKMapRect = MKMapRect.null
    for annotation in mapView.annotations {
        let annotationPoint = MKMapPoint(annotation.coordinate)
        let pointRect = MKMapRect(x: annotationPoint.x, y: annotationPoint.y, width: 0.1, height: 0.1)
        zoomRect = zoomRect.union(pointRect)
    }
    mapView.setVisibleMapRect(zoomRect, animated: true)

// Édité pour swift 5

alicanbatur
la source
4

Grâce à jowie, j'ai mis à jour mon ancienne catégorie vers une solution plus élégante. Partage d'une solution complète, presque prête à copier et coller

MKMapView + AnnotationsRegion.h

#import <MapKit/MapKit.h>

@interface MKMapView (AnnotationsRegion)

-(void)updateRegionForCurrentAnnotationsAnimated:(BOOL)animated;
-(void)updateRegionForCurrentAnnotationsAnimated:(BOOL)animated edgePadding:(UIEdgeInsets)edgePadding;

-(void)updateRegionForAnnotations:(NSArray *)annotations animated:(BOOL)animated;
-(void)updateRegionForAnnotations:(NSArray *)annotations animated:(BOOL)animated edgePadding:(UIEdgeInsets)edgePadding;

@end

MKMapView + AnnotationsRegion.m

#import "MKMapView+AnnotationsRegion.h"

@implementation MKMapView (AnnotationsRegion)

-(void)updateRegionForCurrentAnnotationsAnimated:(BOOL)animated{
    [self updateRegionForCurrentAnnotationsAnimated:animated edgePadding:UIEdgeInsetsZero];
}
-(void)updateRegionForCurrentAnnotationsAnimated:(BOOL)animated edgePadding:(UIEdgeInsets)edgePadding{
    [self updateRegionForAnnotations:self.annotations animated:animated edgePadding:edgePadding];
}

-(void)updateRegionForAnnotations:(NSArray *)annotations animated:(BOOL)animated{
    [self updateRegionForAnnotations:annotations animated:animated edgePadding:UIEdgeInsetsZero];
}
-(void)updateRegionForAnnotations:(NSArray *)annotations animated:(BOOL)animated edgePadding:(UIEdgeInsets)edgePadding{
    MKMapRect zoomRect = MKMapRectNull;
    for(id<MKAnnotation> annotation in annotations){
        MKMapPoint annotationPoint = MKMapPointForCoordinate(annotation.coordinate);
        MKMapRect pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0.1, 0.1);
        zoomRect = MKMapRectUnion(zoomRect, pointRect);
    }
    [self setVisibleMapRect:zoomRect edgePadding:edgePadding animated:animated];
}

@end

J'espère que cela aide quelqu'un et merci encore jowie!

JakubKnejzlik
la source
4
 - (void)zoomMapViewToFitAnnotationsWithExtraZoomToAdjust:(double)extraZoom
{

    if ([self.annotations count] == 0) return;

   int i = 0;
  MKMapPoint points[[self.annotations count]];

   for (id<MKAnnotation> annotation in [self annotations])
  {
      points[i++] = MKMapPointForCoordinate(annotation.coordinate);
   }

  MKPolygon *poly = [MKPolygon polygonWithPoints:points count:i];

MKCoordinateRegion r = MKCoordinateRegionForMapRect([poly boundingMapRect]);
r.span.latitudeDelta += extraZoom;
r.span.longitudeDelta += extraZoom;

[self setRegion: r animated:YES];

}
Ravi Karadbhajane
la source
4

Comme Abhishek Bedi le fait remarquer dans un commentaire, pour iOS7, la meilleure façon de procéder est:

//from API docs: 
//- (void)showAnnotations:(NSArray *)annotations animated:(BOOL)animated NS_AVAILABLE(10_9, 7_0);
[self.mapView showAnnotations:self.mapView.annotations animated:YES];

Pour mon projet personnel (avant iOS7), j'ai simplement ajouté une catégorie sur la classe MKMapView pour encapsuler la fonctionnalité "zone visible" pour une opération très courante: la configurer pour qu'elle puisse voir toutes les annotations actuellement chargées sur l'instance MKMapView ( cela inclut autant de broches que vous auriez pu placer, ainsi que l'emplacement de l'utilisateur). 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. Ainsi, 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
3

Toutes les réponses sur cette page supposent que la carte occupe le plein écran . J'ai en fait un affichage HUD (c'est-à-dire des boutons dispersés en haut et en bas) qui donne des informations sur la carte .. et donc les algorithmes sur la page afficheront les broches bien, mais certains d'entre eux apparaîtront sous les boutons d'affichage HUD .

Ma solution fait un zoom sur la carte pour afficher les annotations dans un sous-ensemble de l'écran et fonctionne pour différentes tailles d'écran (par exemple 3,5 "vs 4,0", etc.):

// create a UIView placeholder and throw it on top of the original mapview
// position the UIView to fit the maximum area not hidden by the HUD display buttons
// add an *other* mapview in that uiview, 
// get the MKCoordinateRegion that fits the pins from that fake mapview
// kill the fake mapview and set the region of the original map 
// to that MKCoordinateRegion.

Voici ce que j'ai fait dans le code (remarque: j'utilise NSConstraintsavec certaines méthodes d'aide pour faire fonctionner mon code dans différentes tailles d'écran .. alors que le code est assez lisible .. ma réponse ici l' explique mieux .. c'est fondamentalement le même flux de travail :)

// position smallerMap to fit available space
// don't store this map, it will slow down things if we keep it hidden or even in memory
[@[_smallerMapPlaceholder] mapObjectsApplyingBlock:^(UIView *view) {
    [view removeFromSuperview];
    [view setTranslatesAutoresizingMaskIntoConstraints:NO];
    [view setHidden:NO];
    [self.view addSubview:view];
}];

NSDictionary *buttonBindingDict = @{ @"mapPlaceholder": _smallerMapPlaceholder};

NSArray *constraints = [@[@"V:|-225-[mapPlaceholder(>=50)]-176-|",
                          @"|-40-[mapPlaceholder(<=240)]-40-|"
                          ] mapObjectsUsingBlock:^id(NSString *formatString, NSUInteger idx){
                              return [NSLayoutConstraint constraintsWithVisualFormat:formatString options:0 metrics:nil views:buttonBindingDict];
                          }];

[self.view addConstraints:[constraints flattenArray]];
[self.view layoutIfNeeded];

MKMapView *smallerMap = [[MKMapView alloc] initWithFrame:self.smallerMapPlaceholder.frame];
[_smallerMapPlaceholder addSubview:smallerMap];

MKCoordinateRegion regionThatFits = [smallerMap getRegionThatFits:self.mapView.annotations];
[smallerMap removeFromSuperview];
smallerMap = nil;
[_smallerMapPlaceholder setHidden:YES];

[self.mapView setRegion:regionThatFits animated:YES];

voici le code qui obtient la région qui correspond:

- (MKCoordinateRegion)getRegionThatFits:(NSArray *)routes {
    MKCoordinateRegion region;
    CLLocationDegrees maxLat = -90.0;
    CLLocationDegrees maxLon = -180.0;
    CLLocationDegrees minLat = 90.0;
    CLLocationDegrees minLon = 180.0;
    for(int idx = 0; idx < routes.count; idx++)
    {
        CLLocation* currentLocation = [routes objectAtIndex:idx];
        if(currentLocation.coordinate.latitude > maxLat)
            maxLat = currentLocation.coordinate.latitude;
        if(currentLocation.coordinate.latitude < minLat)
            minLat = currentLocation.coordinate.latitude;
        if(currentLocation.coordinate.longitude > maxLon)
            maxLon = currentLocation.coordinate.longitude;
        if(currentLocation.coordinate.longitude < minLon)
            minLon = currentLocation.coordinate.longitude;
    }
    region.center.latitude     = (maxLat + minLat) / 2.0;
    region.center.longitude    = (maxLon + minLon) / 2.0;
    region.span.latitudeDelta = 0.01;
    region.span.longitudeDelta = 0.01;

    region.span.latitudeDelta  = ((maxLat - minLat)<0.0)?100.0:(maxLat - minLat);
    region.span.longitudeDelta = ((maxLon - minLon)<0.0)?100.0:(maxLon - minLon);

    MKCoordinateRegion regionThatFits = [self regionThatFits:region];
    return regionThatFits;
}
abbood
la source
2

J'ai fait une petite modification du code de Rafael pour la catégorie MKMapView.

- (void)zoomToFitMapAnnotations {
    if ([self.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 self.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;
    region.span.latitudeDelta = fabs(topLeftCoord.latitude - bottomRightCoord.latitude) * 1.1; // Add a little extra space on the sides
    region.span.longitudeDelta = fabs(bottomRightCoord.longitude - topLeftCoord.longitude) * 1.1; // Add a little extra space on the sides

    [self setRegion:[self regionThatFits:region] animated:YES];
}
Kirow
la source
2

Sur la base des réponses ci-dessus, vous pouvez utiliser une méthode universelle pour zoomer sur la carte afin de s'adapter à toutes les annotations et superpositions en même temps.

-(MKMapRect)getZoomingRectOnMap:(MKMapView*)map toFitAllOverlays:(BOOL)overlays andAnnotations:(BOOL)annotations includeUserLocation:(BOOL)userLocation {
    if (!map) {
        return MKMapRectNull;
    }

    NSMutableArray* overlaysAndAnnotationsCoordinateArray = [[NSMutableArray alloc]init];        
    if (overlays) {
        for (id <MKOverlay> overlay in map.overlays) {
            MKMapPoint overlayPoint = MKMapPointForCoordinate(overlay.coordinate);
            NSArray* coordinate = @[[NSNumber numberWithDouble:overlayPoint.x], [NSNumber numberWithDouble:overlayPoint.y]];
            [overlaysAndAnnotationsCoordinateArray addObject:coordinate];
        }
    }

    if (annotations) {
        for (id <MKAnnotation> annotation in map.annotations) {
            MKMapPoint annotationPoint = MKMapPointForCoordinate(annotation.coordinate);
            NSArray* coordinate = @[[NSNumber numberWithDouble:annotationPoint.x], [NSNumber numberWithDouble:annotationPoint.y]];
            [overlaysAndAnnotationsCoordinateArray addObject:coordinate];
        }
    }

    MKMapRect zoomRect = MKMapRectNull;
    if (userLocation) {
        MKMapPoint annotationPoint = MKMapPointForCoordinate(map.userLocation.coordinate);
        zoomRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0.1, 0.1);
    }

    for (NSArray* coordinate in overlaysAndAnnotationsCoordinateArray) {
        MKMapRect pointRect = MKMapRectMake([coordinate[0] doubleValue], [coordinate[1] doubleValue], 0.1, 0.1);
        zoomRect = MKMapRectUnion(zoomRect, pointRect);
    }

    return zoomRect;
}

Puis:

MKMapRect mapRect = [self getZoomingRectOnMap:mapView toFitAllOverlays:YES andAnnotations:YES includeUserLocation:NO];
[mapView setVisibleMapRect:mapRect edgePadding:UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0) animated:YES];
Alexander Korotkov
la source
1

Je partage juste mes observations à ce sujet:

Si vous utilisez xCode> 6 avec des tailles "déduites" pour les écrans (voir "Mesures simulées" dans l'inspecteur de fichiers) dans le storyboard, appelez

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

dans viewDidLoad entraînera un niveau de zoom trop grand sur les iPhones de 4 pouces car la disposition de la carte est toujours à la taille des écrans plus larges du storyboard.

Vous pouvez déplacer votre appel showAnnotations...vers viewDidAppear. Ensuite, la taille de la carte a déjà été ajustée à l'écran plus petit d'un iPhone 4.

Ou bien changez la valeur "déduit" dans l'inspecteur de fichiers sous "mesures simulées" en iphone 4 pouces.

Jens
la source
1

Vous pouvez sélectionner les formes que vous souhaitez afficher avec les annotations.

extension MKMapView {
  func setVisibleMapRectToFitAllAnnotations(animated: Bool = true,
                                            shouldIncludeUserAccuracyRange: Bool = true,
                                            shouldIncludeOverlays: Bool = true,
                                            edgePadding: UIEdgeInsets = UIEdgeInsets(top: 35, left: 35, bottom: 35, right: 35)) {
    var mapOverlays = overlays

    if shouldIncludeUserAccuracyRange, let userLocation = userLocation.location {
      let userAccuracyRangeCircle = MKCircle(center: userLocation.coordinate, radius: userLocation.horizontalAccuracy)
      mapOverlays.append(MKOverlayRenderer(overlay: userAccuracyRangeCircle).overlay)
    }

    if shouldIncludeOverlays {
      let annotations = self.annotations.filter { !($0 is MKUserLocation) }
      annotations.forEach { annotation in
        let cirlce = MKCircle(center: annotation.coordinate, radius: 1)
        mapOverlays.append(cirlce)
      }
    }

    let zoomRect = MKMapRect(bounding: mapOverlays)
    setVisibleMapRect(zoomRect, edgePadding: edgePadding, animated: animated)
  }
}

extension MKMapRect {
  init(bounding overlays: [MKOverlay]) {
    self = .null
    overlays.forEach { overlay in
      let rect: MKMapRect = overlay.boundingMapRect
      self = self.union(rect)
    }
  }
}
Anirudha Mahale
la source
0

@ "Je ne sais pas si cela est dû à d'autres facteurs dans mon implémentation, mais je trouve que showAnnotations ne fait pas un zoom / ajustement des annotations aussi proche que l'implémentation manuelle, donc je suis resté avec le manuel. - Ted Avery 17 avril à 0:35 "

J'ai eu le même problème, mais j'ai ensuite essayé de faire showAnnotations deux fois (comme ci-dessous), et pour une raison quelconque, cela a fonctionné.

[mapView showAnnotations: votreAnnotationArray animé: OUI]; [mapView showAnnotations: votreAnnotationArray animé: OUI];

zakton
la source
0

Une manière compatible avec iOS 7 consiste à utiliser ce qui suit. Appelez d'abord showAnnotationafin d'obtenir un rectangle comprenant toutes les annotations. Ensuite, créez et UIEdgeInsetavec un insert supérieur de la hauteur de la broche. Ainsi, vous vous assurez d'afficher la broche entière sur la carte.

[self.mapView showAnnotations:self.mapView.annotations animated:YES];
MKMapRect rect = [self.mapView visibleMapRect];
UIEdgeInsets insets = UIEdgeInsetsMake(pinHeight, 0, 0, 0);
[self.mapView setVisibleMapRect:rect edgePadding:insets animated:YES];
Morpheus78
la source
0

Mettez ceci dans votre code en conséquence:

  - (void)mapView:(MKMapView *)mv didAddAnnotationViews:(NSArray *)views
    {
    id<MKAnnotation> mp = [annotationView annotation];
        MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance([mp coordinate] ,250,250);

       [mv setRegion:region animated:YES];

}
Abhishek Bedi
la source