2013-02-16 14 views
9

Sto visualizzando un MKMapView all'interno di un'intestazione di visualizzazione tabella di parallasse in stile Path. Per creare l'effetto, i limiti della mappa sono più grandi dell'area visibile all'utente. Devo impostare la regione di visualizzazione della mappa in modo tale che tutte le annotazioni della mappa siano contenute nel rect visibile di MKMapView. Qual'è il miglior modo per farlo?Visualizza MKMapViewAnnotazioni all'interno del rect visibile della vista mappa

MKMapView with limited visible area

Edit per chiarezza: Ecco un caso d'uso. La dimensione mapView è 320 x 380. L'area visibile, tuttavia, è definita da rect (0.0, 20.0, 320.0, 100.0). Devo impostare la regione in modo tale che tutte le annotazioni vengano visualizzate in questo rect all'interno di mapView.

risposta

15

Impostazione della regione di mappa in modo che tutte le annotazioni sono contenute in una certa parte di un MKMapView può essere fatto in tre fasi. Gli input sono mapView e annotationsFrame.

  1. Calcolare uno MKMapRectmapRect che contiene tutte le annotazioni.
  2. Calcolare gli inserti di imbottitura da mapView.bounds e annotationsFrame.
  3. Chiama -setVisibleMapRect:edgePadding:animated: sulla mappa.

Di seguito è riportata una schermata di un test. La sovrapposizione rossa mostra annotationsFrame.

screen Shot of a test showing that all annotations are inside the given annotation frame

ecco il codice. Beware: è tutto in un unico metodo per semplificare l'aggiunta al codice e non è testato per i casi limite come passare in n annotazioni con la stessa coordinata o avere le annotazioni così distanti che la mappa dovrebbe ottenere ingrandito troppo o avere coordinate che si estendono lungo il bordo della mappa a +/- 180 gradi di longitudine.

- (void)zoomAnnotationsOnMapView:(MKMapView *)mapView toFrame:(CGRect)annotationsFrame animated:(BOOL)animated 
{ 
    if (_mapView.annotations.count < 2) return; 


    // Step 1: make an MKMapRect that contains all the annotations 

    NSArray *annotations = _mapView.annotations; 

    id <MKAnnotation> firstAnnotation = [annotations objectAtIndex:0]; 
    MKMapPoint minPoint = MKMapPointForCoordinate(firstAnnotation.coordinate); 
    MKMapPoint maxPoint = minPoint; 

    for (id <MKAnnotation> annotation in annotations) { 
     MKMapPoint point = MKMapPointForCoordinate(annotation.coordinate); 
     if (point.x < minPoint.x) minPoint.x = point.x; 
     if (point.y < minPoint.y) minPoint.y = point.y; 
     if (point.x > maxPoint.x) maxPoint.x = point.x; 
     if (point.y > maxPoint.y) maxPoint.y = point.y; 
    } 

    MKMapRect mapRect = MKMapRectMake(minPoint.x, minPoint.y, maxPoint.x - minPoint.x, maxPoint.y - minPoint.y); 


    // Step 2: Calculate the edge padding 

    UIEdgeInsets edgePadding = UIEdgeInsetsMake(
     CGRectGetMinY(annotationsFrame), 
     CGRectGetMinX(annotationsFrame), 
     CGRectGetMaxY(mapBounds) - CGRectGetMaxY(annotationsFrame), 
     CGRectGetMaxX(mapBounds) - CGRectGetMaxX(annotationsFrame) 
    ); 


    // Step 3: Set the map rect 

    [mapView setVisibleMapRect:mapRect edgePadding:edgePadding animated:animated]; 
} 

Se si va per un posizionamento perfetto (e chi non lo fa), qui ci sono tre cose da considerare:

  1. Il codice assicura che tutte le coordinate sono in annotationsFrame, ma le annotazioni loro stessi possono essere fuori. Per evitare ciò, è sufficiente utilizzare più padding. Ad esempio, se le tue annotazioni sono 20x20 e centrate sulla coordinata, usa altre 10 imbottiture su tutti i lati.
  2. Sotto iOS 7, la mappa non stava eseguendo lo zoom sulla scala di zoom perfetta, ma sulla dimensione del riquadro successiva (potenza di due). Quindi ci sarà più spazio attorno alle annotazioni del necessario, proprio come mostrato nello screenshot.
  3. Su iOS 7, la vista mappa non solo si ingrandisce perfettamente, ma si preoccupa automaticamente della barra di stato. Per rendere corretto il calcolo, devi sottrarre l'altezza della barra di stato dal padding superiore su iOS 7.
-1

Cerca di ottenere da tutti i valori dei bordi delle annotazioni (massimo e minimo) per lan e lon.

Definire questo valore sul principio:

static float maxLat = FLT_MIN; 
static float maxLon = FLT_MIN; 
static float minLat = FLT_MAX; 
static float minLon = FLT_MAX; 

e quindi utilizzare questa funzione per calcolare arco e della regione:

- (void) zoomAndFit { 
    for(int i = 0; i < [self.points count]; i++) { 
     PRPlaceModel *place = [self.points objectAtIndex:i]; 
     CLLocationCoordinate2D location; 
     location.latitude = [place.lat floatValue]; 
     location.longitude = [place.lon floatValue]; 
     minLat = MIN(minLat, location.latitude); 
     minLon = MIN(minLon, location.longitude); 
     maxLat = MAX(maxLat, location.latitude); 
     maxLon = MAX(maxLon, location.longitude); 
    } 
    MKCoordinateRegion region; 
    MKCoordinateSpan span; 
    span.latitudeDelta = 1.2*(maxLat - minLat); 
    span.longitudeDelta = 1.2*(maxLon - minLon); 

    CLLocationCoordinate2D location; 
    location.latitude = (minLat + maxLat)/2; 
    location.longitude = (minLon + maxLon)/2; 

    region.span=span; 
    region.center=location; 
    [self.mapView setRegion:region animated:TRUE]; 
    [self.mapView regionThatFits:region]; 
} 

e utilizzarlo in modo viewDidLoad:

-(void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    [self zoomAndFit]; 
} 
+0

Questo codice si adatta a tutte le annotazioni nei limiti di mapView, ma ciò di cui ho bisogno è di adattare tutte le annotazioni in un dato CGRect entro i limiti di mapView. – user2393462435

+1

@ user2393462435 Non è esattamente quello che stai cercando di dire ... Leggi tu stesso e sii chiaro in quello che stai chiedendo. –

+1

@ R.A la dimensione mapView è 320 x 380. Impostare la regione del mapview in modo tale che tutte le annotazioni siano all'interno del rect (0.0, 20.0, 320.0, 100.0).Questo è tutto. – user2393462435

0

Se sei pronto ad approssimare i calcoli puoi farlo usando un intelligente ridimensionamento.

L'area di destinazione è 80 di altezza rispetto a una vista della mappa che è 380. Pertanto, si desidera una regione che sia 4,75 volte più alta della regione calcolata per adattarsi alle annotazioni. (0,25 in più sopra e 3,5 in più sotto).

Per prima cosa è necessario ottenere una regione (o maprect, in qualsiasi momento si preferisca lavorare) e ottenere le stesse proporzioni dell'area di destinazione. Questo perché una regione molto ampia e breve non toccherebbe la parte superiore e inferiore dell'area visibile e quindi moltiplicare la sua altezza non renderebbe qualcosa che toccasse la parte superiore e inferiore della vista della mappa. Quindi se viewable_height/viewable_width > annotations_height/annotations_width dovresti impostare annotations_height su annotations_width * (viewable_height/viewable_width).

Con questo si aggiunge il 25% a nord della casella delle annotazioni e il 350% a sud. Puoi farlo spostando il centro al 212,5% (dell'altezza attuale) verso sud e aumentando l'intervallo verticale del 475%.

Ora, tutto questo è un'approssimazione dato che il mondo è sfera e non stiamo guardando una proiezione planare (cioè 1 grado di latitudine vicino all'equatore è disegnato più piccolo di 1 grado vicino ai poli). Ma se vuoi essere accurato, puoi cercare di ridimensionare i numeri in base alla latitudine e così via. Se hai a che fare solo con annotazioni su scala cittadina, probabilmente starai bene.

Spero che questo aiuti.

0

se si desidera trovare le annotazioni che sono in un dato rect:

- (NSArray*)allAnnotationsInMapRect:(MKMapRect)mapRect { 
    NSMutableArray *annotationsInRect = [NSMutableArray array]; 
    for(id<MKAnnotation *ann in self.allAnnotations) { 
     MKMapPoint pt = MKMapPointForCoordinate(ann.coordinate); 
     if(MKMapRectContainsPoint(mapRect, pt)) { 
      [annotationsInRect addObject:ann]; 
     } 
    } 

    return annotationsInRect; 
} 

e per assicurare l'annotazione di vista sono in rect, ottenere la regione per le annotazioni, poi a piedi attraverso di loro e ottenere i limiti di ogni vista vedono se i limiti si adattano all'interno del visibleRect della mappa e se non modificano la regione!

~~ come questo:

- (void)assureAnnotationViewsAreVisible:(NSArray*)annotations originalRegion:(MKCoordinateRegion)originalRegion { 
    CGFloat smallestX = MAXFLOAT; 
    CGFloat smallestY = MAXFLOAT; 
    CGFloat biggestX = -100; 
    CGFloat biggestY = -100; 

    //NSLog(@"---: %d", annotations.count); 
    for(id<MKAnnotation> *annotation in annotations) { 
     UIView *annotationView = [self.mapView viewForAnnotation:v]; 

     CGRect annotationViewFrame = annotationView.bounds; 
     annotationViewFrame.origin = [self.mapView convertCoordinate:annotationView.coordinate toPointToView:self.mapView]; 
     annotationViewFrame.origin = CGPointMake(annotationViewFrame.origin.x-annotationViewFrame.size.width/2, 
               annotationViewFrame.origin.y-annotationViewFrame.size.height); 

     smallestX = MIN(annotationViewFrame.origin.x, smallestX); 
     smallestY = MIN(annotationViewFrame.origin.y, smallestY); 
     biggestX = MAX(annotationViewFrame.origin.x+annotationViewFrame.size.width, biggestX); 
     biggestY = MAX(annotationViewFrame.origin.y+annotationViewFrame.size.height, biggestY); 
    } 
    //NSLog(@"---"); 

    CGRect bounds = self.mapView.bounds; 
    if(smallestX < bounds.origin.x || smallestY < bounds.origin.y || biggestX > bounds.origin.x+bounds.size.width || biggestY > bounds.origin.y+bounds.size.height) { 
     CGRect neededRect = bounds; 
     neededRect.origin = CGPointMake(MIN(bounds.origin.x, smallestX), MIN(bounds.origin.y, smallestY)); 
     neededRect.size = CGSizeMake(MAX(bounds.size.width, biggestX), MAX(bounds.size.height, biggestY)); 

     MKCoordinateRegion neededRegion = [self.mapView convertRect:neededRect toRegionFromView:self.mapView]; 
     _ignoreRegionChange = YES; 
     [self.mapView setRegion:originalRegion animated:NO]; 
     _ignoreRegionChange = NO; 
     [self.mapView setRegion:neededRegion animated:YES]; 
    } 
    else { 
     MKCoordinateRegion currentRegion = self.mapView.region; 
     _ignoreRegionChange = YES; 
     [self.mapView setRegion:originalRegion animated:NO]; 
     _ignoreRegionChange = NO; 
     [self.mapView setRegion:currentRegion animated:YES]; 
    } 
} 
+1

dove allAnnotazioni in un NSArray con le annotazioni che hai (sulla mappa o meno) –

+0

Qual è il valore di MAXFLOAT? –

+0

non lo so. è un valore definito specifico piattaforma/arch/os .. diciamo che è ~ 32000 –

3

A partire da iOS 7.0, questo può essere facilmente ottenuto con showAnnotations.

Swift:

mapView.showAnnotations(mapView.annotations, animated: true) 

Objective-C:

[mapView showAnnotations:mapView.annotations animated:YES]; 

La dichiarazione di cui sopra si regola rect visibile del guarda la mappa in modo da visualizzare tutte le annotazioni.

+0

Nessun metodo esiste per MKMapView ... –

+0

@ kishan94 Stai usando Swift o Objective-C? – Andree

+0

sto usando Objective C ... –

Problemi correlati