Zoom MKMapView para ajustar los pines de anotación?

193

Estoy usando MKMapView y he agregado varios pines de anotación al mapa sobre un área de 5-10 kilómetros. Cuando ejecuto la aplicación, mi mapa comienza a alejarse para mostrar el mundo entero, ¿cuál es la mejor manera de hacer zoom para que los pines se ajusten a la vista?

EDITAR: Mi pensamiento inicial sería usar MKCoordinateRegionMake y calcular el centro de coordenadas, longitudeDelta y latitudeDelta a partir de mis anotaciones. Estoy bastante seguro de que esto funcionará, pero solo quería comprobar que no me faltaba nada obvio.

Código agregado, por cierto: FGLocation es una clase que se ajusta a MKAnnotation, locationFake es uno NSMutableArrayde estos objetos. Los comentarios son siempre bienvenidos ...

- (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
fuente
10
Nota de iOS 7: El nuevo showAnnotations: animated: método puede ayudarlo a evitar este cálculo manual de región.

Respuestas:

123

Lo has entendido bien.

Encuentre sus latitudes y longitudes máximas y mínimas, aplique una aritmética simple y úsela MKCoordinateRegionMake.

Para iOS 7 y superior, use showAnnotations:animated:, desde 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
fuente
158
Para iOS 7 y superior (en referencia a 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
Esto funciona bien, pero ocasionalmente cuando hago zoom (en el mapa) y luego trato de centrarlo (usando un botón que invoca este método) parece que no funciona.
RPM
55
Es importante tener en cuenta que showAnnotationstambién agrega las anotaciones al mapa, incluso si ya existe una anotación para esa ubicación.
Eneko Alonso
@EnekoAlonso Puede solucionar esto llamando removeAnnotations(_ annotations:)inmediatamente despuésshowAnnotations(_ annotations:animated)
Alain Stulz
1
También vale la pena señalar que si bien showAnnotations establece la región para mostrar anotaciones, la región aún se ajusta para que coincida con la relación de aspecto; y esto frecuentemente excluirá algunas de las anotaciones. También tenga en cuenta que showAnnotations es la única solución correcta presentada aquí; ninguna de las otras respuestas intenta siquiera manejar anotaciones que abarcan la línea de fecha internacional.
Gordon Dove
335

Este es el que encontré aquí que me funcionó:

(EDITAR: ¡He actualizado la solución usando la sugerencia de @ Micah para aumentar el pointRect en 0.1 para asegurar que el rect no termine siendo infinitesimalmente pequeño!)

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

 

También puede actualizar esto para incluir el pin de ubicación del usuario reemplazando la primera línea con:

MKMapPoint annotationPoint = MKMapPointForCoordinate(mapView.userLocation.coordinate);
MKMapRect zoomRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0.1, 0.1);
jowie
fuente
44
Ciertamente agradable. Sin embargo, no es necesario que compruebe isNull. MKMapRectUnion hace esto por usted. De los documentos: "Si cualquiera de los rectángulos es nulo, este método devuelve el otro rectángulo".
Felix Lamouroux
37
Muy buena solución !!! Aquí hay un pequeño toque extra para agregar algo de relleno: doble inserción = -zoomRect.size.width * 0.1; [self.mapView setVisibleMapRect: MKMapRectInset (zoomRect, recuadro, recuadro) animado: SÍ];
Craig B
1
¡Increíble! Adición potencial: si desea excluir la 'anotación de ubicación actual', simplemente agregue una instrucción if en el bucle for: if (! [Annotation isKindOfClass: [MKUserLocation class]]) {// Hacer cosas aquí}
kgaidis
2
La solución @CraigB para el relleno es excelente, pero no funciona bien cuando la ruta es vertical, por ejemplo, movimiento de sur a norte, para solucionar este problema use double inset = MIN (-zoomRect.size.width * 0.1, -zoomRect.size. altura * 0.1);
Farhad Malekpour 01 de
1
Mejora con relleno: double insetWidth = -zoomRect.size.width * 0.2; double insetHeight = -zoomRect.size.height * 0.2; MKMapRect insetRect = MKMapRectInset (zoomRect, insetWidth, insetHeight); Luego use este nuevo insertRect
dulgan
121

Apple ha agregado un nuevo método para iOS 7 para simplificar un poco la vida.

[mapView showAnnotations:yourAnnotationArray animated:YES];

Puede extraer fácilmente de una matriz almacenada en la vista de mapa:

yourAnnotationArray = mapView.annotations;

¡y ajuste rápidamente la cámara también!

mapView.camera.altitude *= 1.4;

esto no funcionará a menos que el usuario tenga instalado iOS 7+ u OS X 10.9+. echa un vistazo a la animación personalizada aquí

Ryan Berg
fuente
No estoy seguro de si esto se debe a otros factores en mi implementación, pero creo que eso showAnnotationsno hace un zoom / ajuste de las anotaciones tan cercano como lo hace la implementación manual, así que me quedé con la manual.
Ted Avery
1
intente multiplicar la altitud de la cámara por una fracción de uno, como mapView.camera.altitude * = .85; para una vista más cercana
Ryan Berg
También he encontrado esto útil para seleccionar anotaciones fuera del área visible actual del mapa. Por defecto, MapView no selecciona anotaciones no visibles. Llame a showAnnotations con una matriz de sus anotaciones no visibles, antes de llamar a selectAnnotation, y el mapa debe actualizar su área visible.
MandisaW
42

Utilizo este código y funciona bien para mí:

-(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
fuente
No funciona para: ▿ 2 elementos ▿ 0: CLLocationCoordinate2D - latitud: 46.969995730376894 - longitud: -109.2494943434474 ▿ 1: CLLocationCoordinate2D - latitud: 63.23212154333072 - longitud: 174.13666611126533
Olexiy 'Pyrovva
23

En uso rápido

mapView.showAnnotations(annotationArray, animated: true)

En el objetivo c

[mapView showAnnotations:annotationArray animated:YES];
Sreedeepkesav MS
fuente
2
Si las anotaciones ya se han establecido en mapView, puede hacer referencia a ellas directamente con:mapView.showAnnotations(mapView.annotations, animated: true)
Justin Vallely
14

He convertido la respuesta de Rafael Moreira. El crédito va para él. Para aquellos de ustedes que buscan la versión Swift, aquí está el código:

 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
fuente
14

Swift 3 Esta es la forma correcta de ajustar todas las anotaciones en el mapa.

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
fuente
@ArshadShaik Su edición sugerida ha sido rechazada, si desea proporcionar una nueva respuesta para Swift 4.2, siéntase libre, pero agréguelo como respuesta, no editándolo en la publicación de otro usuario.
Nick
13

La solución de @jowie funciona muy bien. Una trampa, si un mapa tiene solo una anotación, terminará con un mapa completamente alejado. Agregué 0.1 al tamaño de marca rect para asegurarme de que setVisibleMapRect tiene algo a lo que hacer zoom.

MKMapRect pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0.1, 0.1);
Micah
fuente
12

Si está buscando iOS 8 y superior , la forma más sencilla de hacerlo es configurar var layoutMargins: UIEdgeInsets { get set }su vista de mapa antes de llamarfunc showAnnotations(annotations: [MKAnnotation], animated: Bool)

Por ejemplo (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
fuente
12

Creé una extensión para mostrar todas las anotaciones usando un código de aquí y de allá en forma rápida. Esto no mostrará todas las anotaciones si no se pueden mostrar incluso con el nivel de zoom máximo.

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
fuente
Logré obtener mejores resultados alterando los UIEdgeInsetsMakeparámetros, los valores entre 30 y 100 fueron buenos para mí. Estaba probando con el iPhone SE i) S 10.2 Simulator. Ejemplo de código: setVisibleMapRect(zoomRect, edgePadding: UIEdgeInsetsMake(100, 100, 100, 100), animated: true). Como nota, este código funciona en Swift 3 y XCode 8.2.1.
nyxee
8

Se agregó este bucle If dentro del bucle for para excluir el pin de ubicación de los usuarios de este método (requerido en mi caso, y tal vez otros)

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

//Code Here...

}
Sammio2
fuente
8

Para iOS 7 y superior (en referencia a 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);

comentario de - Abhishek Bedi

Tu solo llama:

 [yourMapView showAnnotations:@[yourAnnotation] animated:YES];
Seke1412
fuente
Solo como referencia, el texto NS_AVAILABLE estaba allí porque en enero de 2011, tener iOS 7 en un dispositivo no era muy probable, y NS_AVAILABLE protegió la aplicación contra fallas o fallas de compilación.
Matthew Frederick el
5

En veloz

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

// Editado para swift 5

Alicanbatur
fuente
4

Gracias a Jowie, actualicé mi categoría anterior a una solución más elegante. Compartir una solución completa, casi lista para copiar y pegar

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

Espero que ayude a alguien y gracias de nuevo jowie!

JakubKnejzlik
fuente
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
fuente
4

Como señala Abhishek Bedi en un comentario, para iOS7, la mejor manera de hacerlo es:

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

Para mi proyecto personal (anterior a iOS7) simplemente agregué una categoría en la clase MKMapView para encapsular la funcionalidad de "área visible" para una operación muy común: configurarla para poder ver todas las anotaciones cargadas actualmente en la instancia de MKMapView ( esto incluye tantos pines como haya colocado, así como la ubicación del usuario). el resultado fue este:

archivo .h

#import <MapKit/MapKit.h>

@interface MKMapView (Extensions)

-(void)ij_setVisibleRectToFitAllLoadedAnnotationsAnimated:(BOOL)animated;
-(void)ij_setVisibleRectToFitAnnotations:(NSArray *)annotations animated:(BOOL)animated;


@end

archivo .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

Como puede ver, he agregado 2 métodos hasta ahora: uno para configurar la región visible del mapa en el que se ajusta a todas las anotaciones cargadas actualmente en la instancia de MKMapView, y otro método para configurarlo en cualquier conjunto de objetos. Entonces, para establecer la región visible de mapView, el código sería tan simple como:

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

Espero que ayude =)

Robertibiris
fuente
3

Todas las respuestas en esta página asumen que el mapa ocupa la pantalla completa . De hecho, tengo una pantalla de HUD (es decir, botones dispersos en la parte superior e inferior) que brindan información sobre el mapa ... y los algoritmos en la página mostrarán los pines correctamente, pero algunos de ellos aparecerán debajo los botones de pantalla de HUD .

Mi solución amplía el mapa para mostrar las anotaciones en un subconjunto de la pantalla y funciona para diferentes tamaños de pantalla (es decir, 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.

Esto es lo que hice en el código (nota: lo uso NSConstraintscon algunos métodos auxiliares para hacer que mi código funcione en diferentes tamaños de pantalla ... mientras que el código es bastante legible ... mi respuesta aquí lo explica mejor ... es básicamente el mismo flujo de trabajo :)

// 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];

Aquí está el código que obtiene la región que se ajusta:

- (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
fuente
2

He hecho una pequeña modificación del código de Rafael para la categoría 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
fuente
2

Según las respuestas anteriores, puede utilizar el método universal para hacer zoom en el mapa para que se ajuste a todas las anotaciones y superposiciones al mismo tiempo.

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

Y entonces:

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
fuente
1

Solo compartiendo mis observaciones sobre esto:

Si está utilizando xCode> 6 con tamaños "inferidos" para las pantallas (consulte "métricas simuladas" en el inspector de archivos) en el guión gráfico, llamando

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

en viewDidLoaddará como resultado un nivel de zoom demasiado grande en iPhones con 4 pulgadas porque el diseño del mapa todavía tiene el tamaño de las pantallas más anchas del guión gráfico.

Puede mover su llamada showAnnotations...a viewDidAppear. Luego, el tamaño del mapa ya se ha ajustado a la pantalla más pequeña de un iPhone 4.

O bien, cambie el valor "inferido" en el inspector de archivos en "métricas simuladas" a iphone de 4 pulgadas.

Jens
fuente
1

Puede seleccionar qué formas desea mostrar junto con las anotaciones.

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
fuente
0

@ "No estoy seguro si esto se debe a otros factores en mi implementación, pero encuentro que showAnnotations no hace un zoom / ajuste de las anotaciones tan cerca como lo hace la implementación manual, así que me he quedado con el manual uno. - Ted Avery 17 de abril a las 0:35 "

Tuve el mismo problema, pero luego intenté hacer showAnnotations dos veces (como a continuación), y por alguna razón, funcionó.

[mapView showAnnotations: yourAnnotationArray animado: SÍ]; [mapView showAnnotations: yourAnnotationArray animado: SÍ];

zakton
fuente
0

Una forma compatible con iOS 7 es usar lo siguiente. Primera llamada showAnnotationpara obtener un rectángulo que incluya todas las anotaciones. Luego cree y UIEdgeInsetcon una inserción superior de la altura del pin. Por lo tanto, se asegura de mostrar el pin completo en el mapa.

[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];
Morfeo78
fuente
0

Pon esto en tu código en consecuencia:

  - (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
fuente