Posicionando MKMapView para mostrar várias anotações de uma vez

92

Eu tenho várias anotações que quero adicionar ao meu MKMapView (poderia 0-n itens, onde n é geralmente em torno de 5). Posso adicionar as anotações sem problemas, mas quero redimensionar o mapa para caber todas as anotações na tela de uma vez e não tenho certeza de como fazer isso.

Estive olhando, -regionThatFits:mas não tenho certeza do que fazer com ele. Vou postar algum código para mostrar o que tenho até agora. Acho que isso deve ser uma tarefa geralmente simples, mas estou me sentindo um pouco sobrecarregado com o MapKit até agora.

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

Observe, tudo isso acontece quando recebo uma atualização de local ... Não sei se esse é um lugar apropriado para fazer isso. Se não, qual seria o melhor lugar? -viewDidLoad?

Desde já, obrigado.

Jbrennan
fonte

Respostas:

137

O link postado por Jim agora está morto, mas consegui encontrar o código (que havia marcado em algum lugar). Espero que isto ajude.

- (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
fonte
14
Eu poderia te beijar. Isso só me salvou um monte de tempo. Eu adicionei o código acima para lidar com 1 local. Ficou um pouco próximo e pessoal. Vou postar isso como uma resposta, pois os comentários tendem a mastigar o código.
Michael Reed
Muito obrigado. Eu adicionei isso a uma subclasse de MKMapViewe alterei o método para - (void) zoomToFitAnnotations:(BOOL)animated. Funciona perfeitamente!
simonbs
1
está funcionando muito bem. também é útil. você pode alterar o valor de diminuir ou aumentar o zoom. então region.span.latitudeDelta = fabs (topLeftCoord.latitude - bottomRightCoord.latitude) * 1.1; /// alterar valor. ao aumentar o valor: diminuir o zoom ........ ao diminuir o valor: aumentar o zoom, por exemplo: region.span.latitudeDelta = fabs (topLeftCoord.latitude - bottomRightCoord.latitude) * 4.1;
Erhan Demirci
1
@ MR.Mustafa: Está funcionando, incrível! Mas acho que resolver o problema é o suficiente. Então, por favor, alguém me explique como funciona. Ou através de quaisquer links. Desculpe se sou bobo, sou um iniciante. Suporte de Pls. Obrigado
Siddarth Hailstorm
1
@Mustafa ... Obrigado, salvou meu dia.
Vvk
133

Por que tão 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
fonte
6
inacreditável como isso é muito mais simples, limpo e fácil do que as alternativas publicadas. na verdade, você pode simplificar ainda mais porque não há necessidade de converter para MKCoordinateRegion - basta chamar setVisibleMapRect: em seu MKMapView com o MKMapRect que você cria aqui.
lensovet
2
As anotações às vezes ficam presas no topo do mapa e não são visíveis. Alguma entrada sobre a melhor abordagem para aumentar o zoom após a criação de MKCoordinateRegion?
Kyle C de
3
@KyleC[mapView setVisibleMapRect:mapRect edgePadding:UIEdgeInsetsMake(20.0f, 20.0f, 20.0f, 20.0f) animated:animated];
usuário
Como você cria o CLLocationCoordinate2D *coordsarray? Usando malloc()?
Hlung
3
@KyleC. Eu adicionei antes de retornar, rque basicamente CGFloat zoomOutPercent = 0.2f; r = MKMapRectMake(r.origin.x-r.size.width*zoomOutPercent, r.origin.y-r.size.height*zoomOutPercent, r.size.width*(1+zoomOutPercent*2), r.size.height*(1+zoomOutPercent*2));
diminuiu
44

Fiz algo semelhante a isso para diminuir o zoom (ou aumentar) em uma área que incluía uma anotação de ponto e a localização atual. Você pode expandir isso percorrendo suas anotações.

As etapas básicas são:

  • Calcule o min lat / long
  • Calcular a latitude / longitude máxima
  • Crie objetos CLLocation para esses dois pontos
  • Calcule a distância entre os pontos
  • Crie uma região usando o ponto central entre os pontos e a distância convertida em graus
  • Passe região em MapView para ajustar
  • Use a região ajustada para definir a região 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
fonte
Este parece ser o caminho a percorrer. Obrigado!
jbrennan
1
Conseguiu fazer isso funcionar usando MKCoordinateRegionMake: gist.github.com/1599700 no caso de alguém ainda querer fazer assim.
chakrit
region.center.latitude = (southWest.latitude + northEast.latitude) / 2.0; Obrigado por isso
Tony
Isso funciona com pontos em cada lado do meridiano? O equador?
Eliot
1
Este código coloca os locais fora da tela quando os locais têm um valor y semelhante. Exemplo, mostrar dois locais em (50, -4) e (100, -3) aumentará muito o mapa, colocando as coordenadas fora do lado esquerdo e direito da tela.
usuário
21

Eu tenho uma resposta diferente. Eu ia implementar o algoritmo zoom-to-fit sozinho, mas percebi que a Apple deve ter uma maneira de fazer o que queríamos sem muito trabalho. Usar a API doco rapidamente mostrou que eu poderia usar MKPolygon para fazer o que fosse necessário:

/* 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 isto ajude.

PKCLsoft
fonte
Sem problemas. Geralmente há uma maneira melhor se você estiver disposto e tiver tempo para dedicar a ela.
PKCLsoft
Descobri que isso coloca os pinos um pouco perto demais da borda da tela. Tente adicionar annotationsRegion.span.latitudeDelta = annotationsRegion.span.latitudeDelta * kEventMapDetailBorderFactor; pouco antes de setRegion.
Adam Eberbach
Você está certo @AdamEberbach, mas parece que seu clipe inclui uma constante que não está disponível. Você encontrou um valor que forneceu uma borda "legal" ao redor dos pinos?
PKCLsoft
A resposta do Code Commander abaixo sobre o uso do novo método showAnnotations com iOS7 adiciona uma boa margem, que realmente funciona melhor, embora este código seja mais legal.
James Toomey
14

você também pode fazer assim ..

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

Com base nas informações e sugestões de todos, descobri o seguinte. Obrigado a todos nesta discussão por contribuírem :) Isso iria na visualização Controller que contém o 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
fonte
Isso deve gerar mais votos. Muito preciso e direto ao ponto.
Natasha
5

No meu caso, estou começando com objetos CLLocation e criando anotações para cada um deles.
Eu só preciso colocar duas anotações, então tenho uma abordagem simples para construir a matriz de pontos, mas ela poderia ser facilmente expandida para construir uma matriz com um comprimento arbitrário dado um conjunto de CLLocations.

Esta é minha implementação (não requer a criação 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
fonte
5

Usando Swift, um polígono e algum preenchimento extra, usei o seguinte:

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
fonte
setVisibleMapRect (...). Eu estava fazendo as contas sozinho ... mal.
CodeReaper
3

Há um novo método em 'MKMapView' a partir do iOS 7 que você pode usar

Declaração

RÁPIDO

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

OBJETIVO-C

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

Parâmetros

anotações As anotações que você deseja que sejam visíveis no mapa. animado SIM se deseja que a região do mapa seja animada ou NÃO se deseja que o mapa exiba a nova região imediatamente sem animações.

Discussão

Chamar este método atualiza o valor na propriedade da região e potencialmente outras propriedades para refletir a nova região do mapa.

Matt
fonte
3

Eu sei que esta é uma pergunta antiga, mas se você quiser exibir todas as anotações JÁ NO mapa use isto:

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

Aqui está o equivalente SWIFT (trabalho confirmado em: Xcode6.1, SDK 8.2) para as respostas 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
fonte
1
Ei iOS_Developer. Obrigado pela conversão Swift. Para mim não está funcionando porque acho que estão faltando dois "fmax" em vez do "fmin" para topLeftCoordinate.latitude e bottomRightCoordinate.longitude.
Philipp Otto
2

Uma solução possível pode ser medir a distância entre o local atual e todas as anotações e usar o método MKCoordinateRegionMakeWithDistance para fazer uma região que tem uma distância ligeiramente maior do que a anotação mais distante.

É claro que isso ficaria mais lento quanto mais anotações você adicionasse.

criscokid
fonte
Eu estava passando pela seção de comentários apenas para me validar. Ainda bem que outra pessoa pensa da mesma forma que eu :-) Como adicionei apenas duas anotações (ponto inicial e final), não senti nenhuma lentidão.
thandasoru
2
- (void)zoomToFitMapAnnotations {

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

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

//build array of annotation points
for (id<MKAnnotation> annotation in [self.mapview annotations])
    points[i++] = MKMapPointForCoordinate(annotation.coordinate);

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

[self.mapview setRegion:MKCoordinateRegionForMapRect([poly boundingMapRect]) animated:YES];
}
user3042729
fonte
2

Com base na excelente resposta de me2(agora em 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
fonte
1

Adicionada uma pequena cláusula if para lidar com 1 local - para adicionar ao trecho de código cound de mustufa. Usei a função zoomToAnnotation de pkclSoft para isso:

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

este código funciona para mim, mostra todos os pinos com a localização atual, espero que isso ajude você,

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

Espero que seja pelo menos relevante, é o que eu criei para Mono (baseado na resposta 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); 
}
anulável
fonte
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
fonte
0

Com base na resposta me2, escrevi uma categoria para MKMapView para adicionar algumas margens e pular a anotação de localização do usuário:

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

Como não posso comentar uma resposta, gostaria de acrescentar minha conveniência à resposta de @ me2 (pois achei que era a abordagem mais elegante encontrada aqui).

Para meu projeto pessoal, simplesmente adicionei uma categoria na classe MKMapView para encapsular a funcionalidade de "área visível" para uma operação muito comum: configuração para poder ver todas as anotações carregadas atualmente na instância MKMapView. o resultado foi este:

arquivo .h

#import <MapKit/MapKit.h>

@interface MKMapView (Extensions)

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


@end

arquivo .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 você pode ver, eu adicionei 2 métodos até agora: um para definir a região visível do mapa para aquele que se ajusta a todas as anotações carregadas atualmente na instância de MKMapView e outro método para defini-la para qualquer array de objetos. Portanto, para definir a região visível do mapView, o código seria tão simples como:

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

Espero que ajude =)

Robertibiris
fonte
0

Considere esta extensão:

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

Uma versão 5 rápida:

   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
fonte