Posicionando MKMapView para mostrar múltiples anotaciones a la vez

93

Tengo varias anotaciones que quiero agregar a mi MKMapView (podría incluir elementos 0-n, donde n es generalmente alrededor de 5). Puedo agregar las anotaciones bien, pero quiero cambiar el tamaño del mapa para que se ajuste a todas las anotaciones en pantalla a la vez, y no estoy seguro de cómo hacerlo.

Lo he estado mirando -regionThatFits:pero no estoy muy seguro de qué hacer con él. Publicaré un código para mostrar lo que tengo hasta ahora. Creo que esta debería ser una tarea sencilla en general, pero hasta ahora me siento un poco abrumado con MapKit.

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

Fíjate, todo esto sucede cuando recibo una actualización de ubicación ... No sé si ese es un lugar apropiado para hacer esto. Si no es así, ¿cuál sería un lugar mejor? -viewDidLoad?

Gracias por adelantado.

jbrennan
fuente

Respuestas:

137

El enlace publicado por Jim ahora está muerto, pero pude encontrar el código (que había marcado en algún lugar). Espero que esto ayude.

- (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
fuente
14
Podria besarte. Esto me ahorró un montón de tiempo. Agregué el código al anterior para manejar 1 ubicación. Se puso un poco más cercano y personal. Publicaré eso como respuesta, ya que los comentarios tienden a masticar el código.
Michael Reed
Muchas gracias. Agregué esto a una subclase de MKMapViewy cambié el método a - (void) zoomToFitAnnotations:(BOOL)animated. ¡Funciona perfectamente!
Simonbs
1
está funcionando muy bien. también es útil. puede cambiar el valor de alejar o acercar. entonces region.span.latitudeDelta = fabs (topLeftCoord.latitude - bottomRightCoord.latitude) * 1.1; /// valor de cambio. cuando aumenta el valor: alejar ........ cuando disminuye el valor: acercar, por ejemplo: region.span.latitudeDelta = fabs (topLeftCoord.latitude - bottomRightCoord.latitude) * 4.1;
Erhan Demirci
1
@ MR.Mustafa: ¡Está funcionando, increíble! Pero no creo que resolver el problema sea suficiente. Así que, por favor, alguien me explique cómo funciona. O a través de cualquier enlace. Lo siento si soy tonto, soy un principiante. Por favor, soporte. Gracias
Siddarth Granizada
1
@Mustafa ... Gracias, me salvó el día.
Vvk
133

¿Por qué tan complicado?

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);
}
me2
fuente
6
Increíble lo simple, limpio y fácil que es esto que las alternativas publicadas. de hecho, puede simplificar esto aún más porque no es necesario convertir a MKCoordinateRegion; simplemente llame a setVisibleMapRect: en su MKMapView con el MKMapRect que cree aquí.
lensovet
2
A veces, las anotaciones se pegan en la parte superior del mapa y no son visibles. ¿Alguna información sobre el mejor enfoque para aumentar el zoom después de que se crea MKCoordinateRegion?
Kyle C
3
@KyleC[mapView setVisibleMapRect:mapRect edgePadding:UIEdgeInsetsMake(20.0f, 20.0f, 20.0f, 20.0f) animated:animated];
usuario
¿Cómo creas la CLLocationCoordinate2D *coordsmatriz? Usando malloc()?
Hlung
3
@KyleC. rCGFloat 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));
Agregué
44

Hice algo similar a esto para alejar (o acercar) un área que incluía una anotación de punto y la ubicación actual. Puede expandir esto recorriendo sus anotaciones.

Los pasos básicos son:

  • Calcule el min lat / long
  • Calcule la latitud / longitud máxima
  • Cree objetos CLLocation para estos dos puntos
  • Calcular la distancia entre puntos
  • Cree una región usando el punto central entre los puntos y la distancia convertida a grados
  • Pase la región a MapView para ajustar
  • Use la región ajustada para configurar la región de 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
fuente
Este parece el camino a seguir. ¡Gracias!
jbrennan
1
Se las arregló para que esto funcione usando MKCoordinateRegionMake: gist.github.com/1599700 en caso de que alguien todavía quiera hacerlo de esta manera.
chakrit
region.center.latitude = (southWest.latitude + northEast.latitude) / 2.0; Gracias por ello
Tony
¿Funciona esto con puntos a ambos lados del meridiano? ¿El ecuador?
Eliot
1
Este código coloca las ubicaciones fuera de la pantalla cuando las ubicaciones tienen un valor y similar. Por ejemplo, mostrar dos ubicaciones en (50, -4) y (100, -3) ampliará el mapa demasiado lejos, colocando las coordenadas fuera del lado izquierdo y derecho de la pantalla.
usuario
21

Yo tengo una respuesta diferente. Yo mismo iba a implementar el algoritmo de zoom para ajustar, pero pensé que Apple debía tener una forma de hacer lo que queríamos sin tanto trabajo. El uso de API doco demostró rápidamente que podía usar MKPolygon para hacer lo que se necesitaba:

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

Espero que esto ayude.

PKCLsoft
fuente
No hay problemas. Por lo general, existe una mejor manera si está dispuesto y tiene tiempo para dedicarlo.
PKCLsoft
Descubrí que esto coloca los pines demasiado cerca del borde de la pantalla. Intente agregar annotationsRegion.span.latitudeDelta = annotationsRegion.span.latitudeDelta * kEventMapDetailBorderFactor; justo antes de setRegion.
Adam Eberbach
Tienes razón @AdamEberbach, pero parece que tu clip incluye una constante que no está disponible. ¿Encontró un valor que proporcionó un borde "agradable" alrededor de los pines?
PKCLsoft
La respuesta de Code Commander a continuación sobre el uso del nuevo método showAnnotations con iOS7 agrega un buen margen, que en realidad funciona mejor, aunque este código es más genial.
James Toomey
14

también puedes hacerlo de esta manera ..

// 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
fuente
13

Basado en la información y sugerencias de todos, se me ocurrió lo siguiente. Gracias a todos en esta discusión por contribuir :) Esto iría en la vista Controlador que contiene 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
fuente
Esto debería conseguir más votos. Muy preciso y al grano.
Natasha
5

En mi caso, estoy comenzando con objetos CLLocation y creando anotaciones para cada uno de ellos.
Solo necesito colocar dos anotaciones, por lo que tengo un enfoque simple para construir la matriz de puntos, pero podría expandirse fácilmente para construir una matriz con una longitud arbitraria dado un conjunto de CLLocations.

Aquí está mi implementación (no requiere la creación 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
fuente
5

Usando Swift, un polígono y algo de relleno adicional, usé lo siguiente:

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
fuente
setVisibleMapRect (...). Yo mismo estaba haciendo las matemáticas ... mal.
CodeReaper
3

Hay un nuevo método en 'MKMapView' a partir de iOS 7 que puede usar

Declaración

RÁPIDO

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

C OBJETIVO

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

Parámetros

anotaciones Las anotaciones que desea que estén visibles en el mapa. animado SÍ si desea que el cambio de región del mapa esté animado, o NO si desea que el mapa muestre la nueva región inmediatamente sin animaciones.

Discusión

Llamar a este método actualiza el valor en la propiedad de la región y potencialmente otras propiedades para reflejar la nueva región del mapa.

Mate
fuente
3

Sé que esta es una pregunta antigua pero, si desea mostrar todas las anotaciones YA EN el mapa, use esto:

 mapView.showAnnotations(mapView.annotations, animated: true)
Paul Lehn
fuente
3

Aquí está el equivalente de SWIFT (trabajo confirmado en: Xcode6.1, SDK 8.2) para las respuestas 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
fuente
1
Hola, iOS_Developer. Gracias por la conversión de Swift. Para mí, no funciona porque creo que le faltan dos "fmax" en lugar del "fmin" para topLeftCoordinate.latitude y bottomRightCoordinate.longitude.
Philipp Otto
2

Una posible solución podría ser medir la distancia entre la ubicación actual y todas las anotaciones y usar el método MKCoordinateRegionMakeWithDistance para crear una región que tenga una distancia ligeramente mayor que la anotación más lejana.

Sin embargo, esto, por supuesto, se volverá más lento cuanto más anotaciones agregue.

criscokid
fuente
Estaba revisando la sección de comentarios solo para validarme. Me alegro de que alguien más piense de la manera que yo lo hice :-) Ya que agrego solo dos anotaciones (punto de inicio y final), no sentí ninguna lentitud.
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];
}
usuario3042729
fuente
2

Basado en la excelente respuesta de me2(ahora en 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
fuente
1

Se agregó una pequeña cláusula if para manejar 1 ubicación, para agregar al fragmento de código cound de mustufa. Usé la función zoomToAnnotation de pkclSoft para eso:

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

este código funciona para mí, muestra todos los pines con la ubicación actual, espero que esto te ayude,

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

Espero que esto sea al menos relevante, esto es lo que reuní para Mono (basado en la respuesta 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); 
}
anulable
fuente
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
fuente
0

Basado en la respuesta de me2, escribí una categoría para MKMapView para agregar algunos márgenes y omitir la anotación de ubicación del usuario:

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

Como no puedo comentar una respuesta, me gustaría agregar un poco de conveniencia a la respuesta de @ me2 (ya que pensé que era el enfoque más elegante que se encuentra aquí).

Para mi proyecto personal, simplemente agregué una categoría en la clase MKMapView para encapsular la funcionalidad del "área visible" para una operación muy común: configuración para poder ver todas las anotaciones cargadas actualmente en la instancia MKMapView. 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 la que se ajuste a todas las anotaciones cargadas actualmente en la instancia de MKMapView, y otro método para configurarlo en cualquier matriz 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
0

Considere esta extensión:

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

Una versión rápida 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
fuente
0

Añadiendo más a la respuesta de Stéphane de Luca . Aquí podemos mantener UserLocation más anotaciones personalizadas para adaptarse a 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
}

ingrese la descripción de la imagen aquí

Sreekanth G
fuente