Pozycjonowanie MKMapView w celu jednoczesnego wyświetlania wielu adnotacji

94

Mam kilka adnotacji, które chcę dodać do mojego MKMapView (może to być 0-n elementów, gdzie n wynosi zwykle około 5). Mogę dodać adnotacje w porządku, ale chcę zmienić rozmiar mapy, aby zmieścić wszystkie adnotacje na ekranie jednocześnie, i nie wiem, jak to zrobić.

Patrzyłem, -regionThatFits:ale nie jestem pewien, co z tym zrobić. Wrzucę kod, żeby pokazać, co mam do tej pory. Myślę, że powinno to być ogólnie proste zadanie, ale jak na razie czuję się trochę przytłoczony MapKitem.

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

Zwróć uwagę, to wszystko dzieje się, gdy otrzymuję aktualizację lokalizacji ... Nie wiem, czy to jest odpowiednie miejsce, aby to zrobić. Jeśli nie, gdzie byłoby lepsze miejsce? -viewDidLoad?

Z góry dziękuję.

jbrennan
źródło

Odpowiedzi:

137

Link wysłane przez Jima jest teraz martwy, ale udało mi się znaleźć kod (który miałem zakładką gdzieś). Mam nadzieję że to pomoże.

- (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
źródło
14
Mógłbym cię pocałować. To tylko zaoszczędziło mi trochę czasu. Dodałem kod do powyższego, aby obsłużyć 1 lokalizację. To było trochę bliskie i osobiste. Opublikuję to jako odpowiedź, ponieważ komentarze mają tendencję do przeżuwania kodu.
Michael Reed
Dziękuję Ci bardzo. Dodałem to do podklasy MKMapViewi zmieniłem metodę na - (void) zoomToFitAnnotations:(BOOL)animated. Działa świetnie!
simonbs
1
działa bardzo dobrze. też jest to przydatne. możesz zmienić pomniejszyć lub powiększyć wartość. więc region.span.latitudeDelta = fabs (topLeftCoord.latitude - bottomRightCoord.latitude) * 1.1; /// zmiana waluty. gdy zwiększasz wartość: pomniejsz ........ gdy zmniejszasz wartość: powiększ na przykład: region.span.latitudeDelta = fabs (topLeftCoord.latitude - bottomRightCoord.latitude) * 4.1;
Erhan Demirci,
1
@ MR.Mustafa: To działa, super! Ale myślę, że rozwiązanie problemu nie wystarczy. Więc proszę, niech ktoś mi wytłumaczy, jak to działa. Lub przez jakiekolwiek linki. Przepraszam, jeśli jestem głupi, jestem początkującym. Wsparcie Pls. Dziękuję
Siddarth Hailstorm
1
@Mustafa ... Dzięki, to uratowało mi dzień.
Vvk
133

Dlaczego tak skomplikowane?

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
źródło
6
niewiarygodne, o ile prostsze, czystsze i łatwiejsze jest to niż opublikowane alternatywy. w rzeczywistości możesz to jeszcze bardziej uprościć, ponieważ nie ma potrzeby konwersji na MKCoordinateRegion - po prostu wywołaj setVisibleMapRect: na swoim MKMapView z MKMapRect, który tutaj utworzysz.
lensovet
2
Adnotacje czasami przyklejają się do górnej części mapy i są niewidoczne. Jakieś uwagi dotyczące najlepszego podejścia do zwiększenia powiększenia po utworzeniu regionu MKCoordinateRegion?
Kyle C,
3
@KyleC[mapView setVisibleMapRect:mapRect edgePadding:UIEdgeInsetsMake(20.0f, 20.0f, 20.0f, 20.0f) animated:animated];
użytkownik
Jak tworzysz CLLocationCoordinate2D *coordstablicę? Używasz malloc()?
Hlung
3
@KyleC. Dodałem to przed powrotem, rco w zasadzie pomniejszyło o 20 procentCGFloat 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));
Loozie
45

Zrobiłem coś podobnego do tego, aby pomniejszyć (lub powiększyć) obszar, który zawierał adnotację punktu i bieżącą lokalizację. Możesz to rozwinąć, przeglądając swoje adnotacje w pętli.

Podstawowe kroki to:

  • Oblicz minimalną szerokość / długość
  • Oblicz maksymalną szerokość / długość
  • Utwórz obiekty CLLocation dla tych dwóch punktów
  • Oblicz odległość między punktami
  • Utwórz region, używając punktu środkowego między punktami i odległości przeliczonej na stopnie
  • Przekaż region do MapView, aby dostosować
  • Użyj dostosowanego regionu, aby ustawić region 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
źródło
To wygląda na właściwą drogę. Dzięki!
jbrennan
1
Udało się to uruchomić, używając MKCoordinateRegionMake: gist.github.com/1599700 na wypadek, gdyby ktoś nadal chciał to zrobić w ten sposób.
chakrit
region.center.latitude = (southWest.latitude + northEast.latitude) / 2,0; Dzięki za to
Tony
Czy to działa z punktami po obu stronach południka? Równik?
Eliot
1
Ten kod umieszcza lokalizacje poza ekranem, gdy lokalizacje mają podobną wartość y. Przykład, pokazanie dwóch lokalizacji w (50, -4) i (100, -3) spowoduje zbyt duże powiększenie mapy, umieszczając współrzędne z lewej i prawej strony ekranu.
użytkownik
21

Mam inną odpowiedź. Zamierzałem sam zaimplementować algorytm zoom-to-fit, ale doszedłem do wniosku, że Apple musi mieć sposób na zrobienie tego, co chcieliśmy, bez tak dużego nakładu pracy. Korzystanie z API doco szybko pokazało, że mogę użyć MKPolygon do zrobienia tego, co było potrzebne:

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

Mam nadzieję że to pomoże.

PKCLsoft
źródło
Bez problemów. Zwykle jest lepszy sposób, jeśli chcesz i masz na to czas.
PKCLsoft
Zauważyłem, że powoduje to umieszczenie pinów trochę za blisko krawędzi ekranu. Spróbuj dodać adnotationsRegion.span.latitudeDelta = annotationsRegion.span.latitudeDelta * kEventMapDetailBorderFactor; tuż przed setRegion.
Adam Eberbach
Masz rację @AdamEberbach, ale wydaje się, że Twój klip zawiera stałą, która nie jest dostępna. Czy znalazłeś wartość, która zapewnia „ładne” obramowanie wokół pinów?
PKCLsoft
Poniższa odpowiedź Code Commandera na temat korzystania z nowej metody showAnnotations z iOS7 dodaje ładny margines, który faktycznie działa lepiej, chociaż ten kod jest fajniejszy.
James Toomey,
14

możesz to też zrobić w ten sposób.

// 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
źródło
13

Na podstawie informacji i sugestii wszystkich wymyśliłem następujące. Dziękuję wszystkim uczestnikom tej dyskusji za wkład :) To pójdzie do widoku kontrolera, który zawiera 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
źródło
Powinno to zwiększyć liczbę głosów. Bardzo precyzyjnie i na temat.
Natasha
5

W moim przypadku zaczynam od obiektów CLLocation i tworzę adnotacje dla każdego z nich.
Muszę tylko umieścić dwie adnotacje, więc mam proste podejście do budowania tablicy punktów, ale można ją łatwo rozszerzyć, aby zbudować tablicę o dowolnej długości, biorąc pod uwagę zestaw CLLocations.

Oto moja implementacja (nie wymaga tworzenia 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
źródło
5

Używając języka Swift, wielokąta i dodatkowego wypełnienia, użyłem następującego:

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
źródło
setVisibleMapRect (...). Sam robiłem obliczenia ... słabo.
CodeReaper
4

Oto odpowiednik SWIFT (potwierdzony działanie w: Xcode6.1, SDK 8.2) dla odpowiedzi Mustafy:

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
źródło
1
Hej iOS_Developer. Dzięki za szybką konwersję. Dla mnie to nie działa, ponieważ myślę, że brakuje ci dwóch "fmax" zamiast "fmin" dla topLeftCoordinate.latitude i bottomRightCoordinate.longitude.
Philipp Otto
3

Istnieje nowa metoda w „MKMapView” od iOS 7, której możesz użyć

Deklaracja

SZYBKI

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

CEL C

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

Parametry

adnotations Adnotacje, które mają być widoczne na mapie. animowany TAK, jeśli chcesz, aby zmiana regionu mapy była animowana, lub NIE, jeśli chcesz, aby mapa natychmiast wyświetlała nowy region bez animacji.

Dyskusja

Wywołanie tej metody aktualizuje wartość we właściwości regionu i potencjalnie innych właściwościach, aby odzwierciedlić nowy region mapy.

Matt
źródło
3

Wiem, że to stare pytanie, ale jeśli chcesz wyświetlić wszystkie adnotacje JUŻ NA mapie, użyj tego:

 mapView.showAnnotations(mapView.annotations, animated: true)
Paul Lehn
źródło
2

Jednym z możliwych rozwiązań może być pomiar odległości między bieżącą lokalizacją a wszystkimi adnotacjami i użycie metody MKCoordinateRegionMakeWithDistance w celu utworzenia regionu, który ma nieco większą odległość niż najdalsza adnotacja.

Oczywiście będzie to wolniejsze, im więcej dodasz adnotacji.

criscokid
źródło
Przeglądałem sekcję komentarzy tylko po to, aby się zweryfikować. Cieszę się, że ktoś inny myśli tak, jak ja :-) Ponieważ dodałem tylko dwie adnotacje (początek i koniec), nie czułem żadnej powolności.
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
źródło
2

Na podstawie doskonałej odpowiedzi autorstwa me2(teraz w języku 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
źródło
1

Dodano małą klauzulę if do obsługi 1 lokalizacji - do dodania do fragmentu kodu cound mustufa. Wykorzystano do tego funkcję zoomToAnnotation pkclSoft:

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
źródło
1

ten kod działa u mnie, pokazuje wszystkie piny z aktualną lokalizacją, mam nadzieję, że to ci pomoże,

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
źródło
1

Dodanie dodatkowych do odpowiedzi Stéphane de Luca . Tutaj możemy zachować UserLocation oraz niestandardowe adnotacje, aby pasowały do ​​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
}

wprowadź opis obrazu tutaj

Sreekanth G
źródło
0

Mam nadzieję, że jest to przynajmniej istotne, oto co złożyłem razem dla Mono (na podstawie odpowiedzi 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); 
}
nullable
źródło
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
źródło
0

Na podstawie odpowiedzi me2 napisałem kategorię dla MKMapView, aby dodać marginesy i pominąć adnotację lokalizacji użytkownika:

@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
źródło
0

Ponieważ nie mogę skomentować odpowiedzi, chciałbym dodać trochę wygody do odpowiedzi @ me2 (ponieważ uważałem, że to najbardziej eleganckie podejście znalezione tutaj).

W moim osobistym projekcie po prostu dodałem kategorię do klasy MKMapView, aby zamknąć funkcjonalność „widocznego obszaru” dla typowej operacji: ustawienie, aby móc zobaczyć wszystkie aktualnie załadowane adnotacje w instancji MKMapView. wynik był taki:

plik .h

#import <MapKit/MapKit.h>

@interface MKMapView (Extensions)

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


@end

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

Jak widać, do tej pory dodałem 2 metody: jedną do ustawiania widocznego regionu mapy na tę, która pasuje do wszystkich aktualnie załadowanych adnotacji w instancji MKMapView, oraz inną do ustawiania jej na dowolną tablicę obiektów. Aby więc ustawić widoczny region mapView, kod byłby tak prosty, jak:

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

Mam nadzieję, że to pomoże =)

Robertibiris
źródło
0

Rozważ to rozszerzenie:

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
źródło
0

Swift 5 wersja:

   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
źródło