Позиционирование MKMapView для одновременного отображения нескольких аннотаций

94

У меня есть несколько аннотаций, которые я хочу добавить в свой MKMapView (это может быть от 0 до n элементов, где n обычно составляет около 5). Я могу добавить аннотации в порядке, но я хочу изменить размер карты, чтобы она соответствовала всем аннотациям на экране сразу, и я не знаю, как это сделать.

Я смотрел, -regionThatFits:но не совсем уверен, что с этим делать. Я отправлю код, чтобы показать, что у меня есть на данный момент. Я думаю, что это должна быть в целом простая задача, но пока я чувствую себя немного перегруженным MapKit.

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{

location = newLocation.coordinate;
//One location is obtained.. just zoom to that location

MKCoordinateRegion region;
region.center = location;

//Set Zoom level using Span
MKCoordinateSpan span;
span.latitudeDelta = 0.015;
span.longitudeDelta = 0.015;
region.span = span;
// Set the region here... but I want this to be a dynamic size
// Obviously this should be set after I've added my annotations
[mapView setRegion:region animated:YES];

// Test data, using these as annotations for now
NSArray *arr = [NSArray arrayWithObjects:@"one", @"two", @"three", @"four", nil];
float ex = 0.01;
for (NSString *s in arr) {
    JBAnnotation *placemark = [[JBAnnotation alloc] initWithLat:(location.latitude + ex) lon:location.longitude];
    [mapView addAnnotation:placemark];
    ex = ex + 0.005;
}
    // What do I do here?
    [mapView setRegion:[mapView regionThatFits:region] animated:YES];
}

Заметьте, все это происходит, когда я получаю обновление местоположения ... Я не знаю, подходит ли это место для этого. Если нет, то где было бы лучше? -viewDidLoad?

Заранее спасибо.

Джбреннан
источник

Ответы:

137

Ссылка Написала Джим теперь мертва, но я был в состоянии найти код (который я имел закладку где - то). Надеюсь это поможет.

- (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]; 
}
Мустафа
источник
14
Я мог бы тебя поцеловать. Это просто сэкономило мне кучу времени. Я добавил код к вышеупомянутому, чтобы обработать 1 место. Это стало немного более личным. Я отправлю это как ответ, поскольку комментарии, как правило, искажают код.
Майкл Рид
Большое спасибо. Я добавил это в подкласс MKMapViewи изменил метод на - (void) zoomToFitAnnotations:(BOOL)animated. Прекрасно работает!
simonbs
1
работает очень хорошо. также это полезно. вы можете уменьшить или увеличить значение. итак region.span.latitudeDelta = fabs (topLeftCoord.latitude - bottomRightCoord.latitude) * 1.1; /// изменить значение. при увеличении значения: уменьшение масштаба ........ при уменьшении значения: увеличение масштаба, например: region.span.latitudeDelta = fabs (topLeftCoord.latitude - bottomRightCoord.latitude) * 4.1;
Erhan Demirci
1
@ MR.Mustafa: Это работает, круто! Но я не думаю, что решения вопроса достаточно. Так что, пожалуйста, объясните мне, как это работает. Или по любым ссылкам. Извините, если я дурак, я новичок. Пожалуйста, поддержите. Спасибо
Siddarth Hailstorm
1
@Mustafa ... Спасибо, это спасло мне день.
Vvk
133

Почему так сложно?

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);
}
мне2
источник
6
невероятно, насколько это проще, чище и проще, чем опубликованные альтернативы. Фактически, вы можете упростить это еще больше, потому что нет необходимости преобразовывать в MKCoordinateRegion - просто вызовите setVisibleMapRect: в своем MKMapView с помощью MKMapRect, который вы здесь создаете.
линзовет
2
Аннотации иногда прикрепляются к верхней части карты и не видны. Какие-либо данные о наилучшем подходе к увеличению масштабирования после создания MKCoordinateRegion?
Kyle C
3
@KyleC[mapView setVisibleMapRect:mapRect edgePadding:UIEdgeInsetsMake(20.0f, 20.0f, 20.0f, 20.0f) animated:animated];
пользователь
Как создать CLLocationCoordinate2D *coordsмассив? Используя malloc()?
Hlung
3
@KyleC. Я добавил это перед тем, как вернуться, rчто в основном 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));
уменьшает
45

Я сделал что-то похожее на это, чтобы уменьшить (или увеличить) область, содержащую аннотацию точки и текущее местоположение. Вы можете расширить это, просматривая свои аннотации.

Основные шаги:

  • Рассчитайте мин. Шир. / Долг.
  • Рассчитайте максимальную широту / долготу
  • Создайте объекты CLLocation для этих двух точек
  • Рассчитать расстояние между точками
  • Создайте область, используя центральную точку между точками и расстояние, преобразованное в градусы
  • Передайте регион в MapView для настройки
  • Используйте скорректированную область, чтобы установить область 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];
    }
Дональд Берд
источник
Похоже, это правильный путь. Благодарность!
jbrennan
1
Удалось заставить это работать с помощью MKCoordinateRegionMake: gist.github.com/1599700 на случай, если кто-то все еще захочет сделать это таким образом.
chakrit
region.center.latitude = (юг-запад. широта + север-вост. шир.) / 2.0; Спасибо за это
Тони
Работает ли это с точками по обе стороны от меридиана? Экватор?
Элиот
1
Этот код помещает местоположения за пределы экрана, когда у местоположений одинаковое значение y. Например, отображение двух местоположений в точках (50, -4) и (100, -3) приведет к слишком большому масштабированию карты, размещая координаты с левой и правой стороны экрана.
пользователь
21

У меня другой ответ. Я собирался сам реализовать алгоритм масштабирования до подгонки, но решил, что у Apple должен быть способ делать то, что мы хотели, без особых усилий. Использование API doco быстро показало, что я могу использовать MKPolygon, чтобы делать то, что было необходимо:

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

Надеюсь это поможет.

PKCLsoft
источник
Без проблем. Обычно есть лучший способ, если у вас есть время, чтобы на него потратить.
PKCLsoft,
Я обнаружил, что при этом контакты располагаются слишком близко к краю экрана. Попробуйте добавить annotationsRegion.span.latitudeDelta = annotationsRegion.span.latitudeDelta * kEventMapDetailBorderFactor; непосредственно перед setRegion.
Адам Эбербах
Вы правы, @AdamEberbach, но похоже, что ваш клип содержит недоступную константу. Вы нашли значение, обеспечивающее "красивую" рамку вокруг контактов?
PKCLsoft
Ответ Code Commander ниже об использовании нового метода showAnnotations с iOS7 добавляет хороший запас, который на самом деле работает лучше, хотя этот код круче.
Джеймс Туми
14

вы также можете сделать это так ..

// 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;
}
Маниш Ахуджа
источник
13

Основываясь на информации и предложениях от всех, я пришел к следующему. Спасибо всем участникам этого обсуждения за участие :) Это будет происходить в контроллере представления, который содержит 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
источник
Это должно получить больше голосов. Очень точно и по делу.
Наташа
6

Используя Swift, многоугольник и дополнительные отступы, я использовал следующее:

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)
}

Линдси Турмонд
источник
setVisibleMapRect (...). Я сам подсчитывал ... плохо.
CodeReaper
5

В моем случае я начинаю с объектов CLLocation и создаю аннотации для каждого из них.
Мне нужно разместить только две аннотации, поэтому у меня есть простой подход к построению массива точек, но его можно легко расширить, чтобы построить массив произвольной длины с учетом набора CLLocations.

Вот моя реализация (не требует создания 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])];
Якобсимеон
источник
4

Вот эквивалент SWIFT (подтвержден в: Xcode6.1, SDK 8.2) для ответов Мустафы:

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)

}
Сару
источник
1
Привет, iOS_Developer. Спасибо за преобразование Swift. Для меня это не работает, потому что я думаю, вам не хватает двух «fmax» вместо «fmin» для topLeftCoordinate.latitude и bottomRightCoordinate.longitude.
Филипп Отто
3

В iOS 7 появился новый метод MKMapView, который вы можете использовать

Декларация

SWIFT

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

ЦЕЛЬ-C

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

Параметры

аннотации Аннотации, которые вы хотите видеть на карте. анимированный ДА, если вы хотите, чтобы изменение региона карты было анимированным, или НЕТ, если вы хотите, чтобы карта отображала новый регион немедленно без анимации.

Обсуждение

Вызов этого метода обновляет значение свойства региона и, возможно, других свойств, чтобы отразить новую область карты.

Мэтт
источник
3

Я знаю, что это старый вопрос, но если вы хотите отобразить все аннотации УЖЕ НА карте, используйте это:

 mapView.showAnnotations(mapView.annotations, animated: true)
Пол Лен
источник
2

Одним из возможных решений может быть измерение расстояния между текущим местоположением и всеми аннотациями и использование метода MKCoordinateRegionMakeWithDistance для создания области, которая имеет немного большее расстояние, чем самая дальняя аннотация.

Это, конечно, будет медленнее, чем больше вы добавите аннотаций.

Criscokid
источник
Я просматривал раздел комментариев только для того, чтобы проверить себя. Рад, что кто-то думает так же, как и я :-) Поскольку я добавляю только две аннотации (начало и конец), я не чувствовал медлительности.
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
источник
2

На основе отличного ответа me2(теперь в 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)
}
тило
источник
1

Добавлено небольшое условие if для обработки 1 местоположения - для добавления во фрагмент кода cound от mustufa. Для этого использовалась функция pkclSoft zoomToAnnotation:

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
}
Майкл Рид
источник
1

этот код работает для меня, он показывает все контакты с текущим местоположением, надеюсь, это поможет вам,

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)

}
Патель Джигар
источник
1

Добавление дополнительного к ответу Стефана де Луки . Здесь мы можем сохранить UserLocation плюс пользовательские аннотации, чтобы они соответствовали 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
}

введите описание изображения здесь

Sreekanth G
источник
0

Надеюсь, это по крайней мере актуально, это то, что я собрал для Mono (на основе ответа 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); 
}
обнуляемый
источник
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
источник
0

На основании ответа me2 я написал категорию для MKMapView, чтобы добавить некоторые поля и пропустить аннотацию местоположения пользователя:

@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
Томаш
источник
0

Поскольку я не могу прокомментировать ответ, я хотел бы добавить немного удобства в ответ @me2 (так как я думал, что это самый элегантный подход, найденный здесь).

В моем личном проекте я просто добавил категорию в класс MKMapView, чтобы инкапсулировать функциональность «видимой области» для более общей операции: настройка, позволяющая видеть все загруженные в данный момент аннотации в экземпляре MKMapView. Результат был такой:

.h файл

#import <MapKit/MapKit.h>

@interface MKMapView (Extensions)

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


@end

.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

Как видите, на данный момент я добавил 2 метода: один для установки видимой области карты на тот, который соответствует всем загруженным в данный момент аннотациям в экземпляре MKMapView, и другой метод для установки его на любой массив объектов. Таким образом, чтобы установить видимую область mapView, код будет таким простым, как:

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

Надеюсь поможет =)

Робертибирис
источник
0

Рассмотрим это расширение:

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
источник
0

Быстрая версия 5:

   func regionFor(coordinates coords: [CLLocationCoordinate2D]) -> MKCoordinateRegion {
        var r = MKMapRect.null

        for i in 0 ..< coords.count {
            let p = MKMapPoint(coords[i])

            r = r.union(MKMapRect(x: p.x, y: p.y, width: 0, height: 0))
        }

        return MKCoordinateRegion(r)
    }
Стефан де Лука
источник