Zoom MKMapView para ajustar pinos de anotação?

193

Estou usando o MKMapView e adicionei vários pinos de anotação ao mapa em uma área de 5 a 10 quilômetros. Quando executo o aplicativo, meu mapa começa com o zoom reduzido para mostrar o mundo inteiro, qual é a melhor maneira de aplicar zoom no mapa para que os pinos se ajustem à visualização?

Edição: Meu pensamento inicial seria usar MKCoordinateRegionMake e calcular o centro de coordenadas, longitudeDelta e latitudeDelta das minhas anotações. Tenho certeza de que isso funcionará, mas eu só queria verificar se não estava perdendo nada óbvio.

Código adicionado, BTW: FGLocation é uma classe em conformidade com MKAnnotation, locationFake é um NSMutableArraydesses objetos. Comentários são sempre bem-vindos ....

- (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
fonte
10
Nota para o iOS 7: o novo método showAnnotations: animated: pode ajudar a evitar esse cálculo manual da região.

Respostas:

123

Você acertou.

Encontre suas latitudes e longitudes máximas e mínimas, aplique aritmética simples e use MKCoordinateRegionMake.

Para iOS 7 e superior, use showAnnotations:animated:, 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
fonte
158
Para iOS 7 e superior (Referindo-se ao 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
Isso funciona bem, mas ocasionalmente quando eu aumento o zoom (no mapa) e tento centralizá-lo (usando um botão que chama esse método), ele parece não funcionar.
RPM
5
É importante observar que showAnnotationstambém adiciona as anotações ao mapa, mesmo que já exista uma anotação para esse local.
Eneko Alonso
@EnekoAlonso Você pode contornar isso ligando removeAnnotations(_ annotations:)imediatamente apósshowAnnotations(_ annotations:animated)
Alain Stulz
1
Também digno de nota é que, enquanto showAnnotations define a região para exibir anotações, a região ainda é ajustada para corresponder à proporção; e isso frequentemente exclui algumas das anotações. Observe também que showAnnotations é a única solução correta apresentada aqui; nenhuma das outras respostas tenta manipular anotações que abrangem a linha de data internacional.
Gordon Dove
335

Foi o que encontrei aqui que funcionou para mim:

(EDIT: eu atualizei a solução usando a sugestão de @ Micah para aumentar o pointRect em 0,1 para garantir que o rect não acabe sendo infinitesimalmente pequeno!)

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

 

Você também pode atualizar isso para incluir o pino userLocation substituindo a primeira linha por:

MKMapPoint annotationPoint = MKMapPointForCoordinate(mapView.userLocation.coordinate);
MKMapRect zoomRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0.1, 0.1);
jowie
fonte
4
Bom mesmo. Você não precisa verificar isNull. MKMapRectUnion faz isso por você. Na documentação: "Se um retângulo for nulo, esse método retornará o outro retângulo".
Felix Lamouroux
37
Very nice solution !!! Aqui está um pequeno toque extra para adicionar um pouco de preenchimento: double inset = -zoomRect.size.width * 0.1; [self.mapView setVisibleMapRect: MKMapRectInset (zoomRect, inserção, inserção) animado: SIM];
Craig B #
1
Impressionante! Adição potencial: se você deseja excluir a 'anotação de local atual', adicione uma instrução if no loop for: if (! [Anotação éKindOfClass: [classe MKUserLocation]]) {// Faça as coisas aqui}
kgaidis
2
A solução do @CraigB para preenchimento é excelente, mas não funciona bem quando o caminho é vertical, por exemplo, movimento do sul para o norte, para corrigir isso, use double inset = MIN (-zoomRect.size.width * 0.1, -zoomRect.size. altura * 0,1);
Farhad Malekpour
1
Aprimoramento com preenchimento: double insetWidth = -zoomRect.size.width * 0.2; double insetHeight = -zoomRect.size.height * 0.2; MKMapRect insetRect = MKMapRectInset (zoomRect, insetWidth, insetHeight); Em seguida, use este novo insertRect
dulgan 6/15
121

A Apple adicionou um novo método para o IOS 7 para simplificar um pouco a vida.

[mapView showAnnotations:yourAnnotationArray animated:YES];

Você pode facilmente extrair de uma matriz armazenada na visualização do mapa:

yourAnnotationArray = mapView.annotations;

e ajuste rapidamente a câmera também!

mapView.camera.altitude *= 1.4;

isso não funcionará, a menos que o usuário tenha o iOS 7+ ou OS X 10.9+ instalado. confira animação personalizada aqui

Ryan Berg
fonte
Não tenho certeza se isso é devido a alguns outros fatores em minha implementação, mas acho que showAnnotationsisso não faz com que as anotações se aproximem mais do que a implementação manual, por isso fiquei com a manual.
Ted Avery
1
tente multiplicar a altitude da câmera por uma fração de uma, como mapView.camera.altitude * = .85; para uma janela mais perto
Ryan Berg
Também achei isso útil para selecionar anotações fora da área atual do mapa visível. Por padrão, o MapView não seleciona anotações não visíveis. Chame showAnnotations com uma matriz de anotações não visíveis antes de chamar selectAnnotation, e o mapa deve atualizar sua área visível.
precisa saber é o seguinte
42

Eu uso este código e funciona bem para mim:

-(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
fonte
Não trabalhe para: ▿ 2 elementos ▿ 0: CLLocationCoordinate2D - latitude: 46.969995730376894 - longitude: -109.2494943434474 ▿ 1: CLLocationCoordinate2D - latitude: 63.23212154333072 - longitude: 174.13666611126533
Olexiy Pyvovarov
23

Em uso rápido

mapView.showAnnotations(annotationArray, animated: true)

No objetivo c

[mapView showAnnotations:annotationArray animated:YES];
Sreedeepkesav MS
fonte
2
Se as anotações já tiverem sido definidas no mapView, você poderá referenciá-las diretamente com:mapView.showAnnotations(mapView.annotations, animated: true)
Justin Vallely
14

Eu converti a resposta por Rafael Moreira. O crédito vai para ele. Para aqueles que procuram a versão Swift, aqui está o 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
fonte
14

Swift 3 Esta é a maneira correta de ajustar todas as anotações no 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
fonte
@ArshadShaik Sua edição sugerida foi rejeitada. Se você deseja fornecer uma nova resposta para o Swift 4.2, sinta-se à vontade, mas adicione-a como resposta, não editando-a em outra postagem de usuários.
Nick
13

A solução da @ jowie funciona muito bem. Uma captura, se um mapa tiver apenas uma anotação, você terminará com um mapa totalmente ampliado. Adicionei 0,1 ao tamanho de retângulo para garantir que setVisibleMapRect tenha algo para ampliar.

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

Se você procura o iOS 8 e superior , a maneira mais simples de fazer isso é definir a var layoutMargins: UIEdgeInsets { get set }visualização do mapa antes de ligarfunc showAnnotations(annotations: [MKAnnotation], animated: Bool)

Por exemplo (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
fonte
12

Criei uma extensão para mostrar todas as anotações usando algum código daqui e dali rapidamente. Isso não mostrará todas as anotações se elas não puderem ser mostradas, mesmo no nível máximo de zoom.

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
fonte
Consegui obter melhores resultados alterando os UIEdgeInsetsMakeparâmetros, valores entre 30 e 100 foram bons para mim. Eu estava testando usando o iPhone SE i) S 10.2 Simulator. Código exemplo: setVisibleMapRect(zoomRect, edgePadding: UIEdgeInsetsMake(100, 100, 100, 100), animated: true). Como uma observação, este código funciona no Swift 3 e no XCode 8.2.1.
nyxee
8

Adicionado este loop If no loop for para excluir o pino de localização dos usuários desse método (obrigatório no meu caso e talvez outros)

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

//Code Here...

}
Sammio2
fonte
8

Para iOS 7 e superior (referente ao 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);

observação de - Abhishek Bedi

Você acabou de ligar para:

 [yourMapView showAnnotations:@[yourAnnotation] animated:YES];
Seke1412
fonte
Apenas para referência, o texto NS_AVAILABLE estava lá porque, em janeiro de 2011, o iOS 7 em um dispositivo não era muito provável, e o NS_AVAILABLE protegeu o aplicativo contra falhas ou travamentos na compilação.
Matthew Frederick
5

Na 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
fonte
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 o swift 5

alicanbatur
fonte
4

Graças a jowie, atualizei minha categoria antiga para uma solução mais elegante. Compartilhamento de solução completa, quase copiar e colar

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 ajude alguém e obrigado novamente jowie!

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

Como Abhishek Bedi aponta em um comentário, para iOS7, a melhor maneira de fazer isso é:

//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 o meu projeto pessoal (antes do iOS7), simplesmente adicionei uma categoria na classe MKMapView para encapsular a funcionalidade "área visível" para uma operação muito comum: configurando-a para poder ver todas as anotações atualmente carregadas na instância do MKMapView ( isso inclui quantos pinos você tiver colocado, bem como a localização do usuário). o resultado foi o seguinte:

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, adicionei dois métodos até agora: um para definir a região visível do mapa para a que se encaixa em todas as anotações atualmente carregadas na instância MKMapView e outro para defini-lo para qualquer matriz de objetos. Portanto, para definir a região visível do mapView, o código seria tão simples quanto:

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

Espero que ajude =)

Robertibiris
fonte
3

Todas as respostas nesta página assumem que o mapa ocupa a tela inteira . Na verdade, eu tenho uma tela HUD (ou seja, botões espalhados na parte superior e inferior) que fornece informações sobre o mapa. Portanto, os algoritmos da página exibem os pinos corretamente, mas alguns deles aparecerão em os botões da tela HUD .

Minha solução amplia o mapa para exibir as anotações em um subconjunto da tela e funciona para diferentes tamanhos de tela (por exemplo, 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.

Aqui está o que eu fiz no código (nota: eu uso NSConstraintscom alguns métodos auxiliares para fazer meu código funcionar em diferentes tamanhos de tela .. enquanto o código é bastante legível .. minha resposta aqui explica melhor .. é basicamente o mesmo fluxo de trabalho :)

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

Aqui está o código que obtém a região que se encaixa:

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

Fiz uma pequena modificação no código de Rafael para a categoria 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
fonte
2

Com base nas respostas acima, você pode usar o método universal para ampliar o mapa para caber em todas as anotações e sobreposições ao mesmo tempo.

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

E depois:

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

Apenas compartilhando minhas observações sobre isso:

Se você estiver usando xCode> 6 com tamanhos "inferidos" para as telas (consulte "métricas simuladas" no inspetor de arquivos) no storyboard, chamando

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

in viewDidLoadresultará em um nível de zoom muito grande em iPhones de 4 polegadas, porque o layout do mapa ainda é do tamanho das telas mais amplas do storyboard.

Você pode mover sua chamada para showAnnotations...para viewDidAppear. Em seguida, o tamanho do mapa já foi ajustado para a tela menor de um iPhone 4.

Ou, alternativamente, altere o valor "inferido" no inspetor de arquivos em "métricas simuladas" para iphone de 4 polegadas.

Jens
fonte
1

Você pode selecionar quais formas deseja mostrar junto com as anotações.

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

@ "Não tenho certeza se isso é devido a alguns outros fatores na minha implementação, mas acho que showAnnotations não faz o zoom / ajuste das anotações mais do que a implementação manual, então fiquei com o manual. - Ted Avery 17 de abril às 0:35 "

Eu tive o mesmo problema, mas tentei fazer o showAnnotations duas vezes (como abaixo) e, por algum motivo, funcionou.

[mapView showAnnotations: yourAnnotationArray animated: YES]; [mapView showAnnotations: yourAnnotationArray animated: YES];

zakton
fonte
0

Uma maneira compatível com o iOS 7 é usar o seguinte. Primeira chamada showAnnotationpara obter um retângulo, incluindo todas as anotações. Depois, crie e UIEdgeInsetcom uma inserção superior da altura do pino. Assim, você garante mostrar o pino inteiro no 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];
Morpheus78
fonte
0

Coloque isso no seu código de acordo:

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