У меня есть несколько аннотаций, которые я хочу добавить в свой 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
?
Заранее спасибо.
источник
MKMapView
и изменил метод на- (void) zoomToFitAnnotations:(BOOL)animated
. Прекрасно работает!Почему так сложно?
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); }
источник
[mapView setVisibleMapRect:mapRect edgePadding:UIEdgeInsetsMake(20.0f, 20.0f, 20.0f, 20.0f) animated:animated];
CLLocationCoordinate2D *coords
массив? Используяmalloc()
?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));
Я сделал что-то похожее на это, чтобы уменьшить (или увеличить) область, содержащую аннотацию точки и текущее местоположение. Вы можете расширить это, просматривая свои аннотации.
Основные шаги:
-(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]; }
источник
MKCoordinateRegionMake
: gist.github.com/1599700 на случай, если кто-то все еще захочет сделать это таким образом.У меня другой ответ. Я собирался сам реализовать алгоритм масштабирования до подгонки, но решил, что у 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]; }
Надеюсь это поможет.
источник
вы также можете сделать это так ..
// 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; }
источник
Основываясь на информации и предложениях от всех, я пришел к следующему. Спасибо всем участникам этого обсуждения за участие :) Это будет происходить в контроллере представления, который содержит 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]; }
источник
Используя 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) }
источник
В моем случае я начинаю с объектов 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])];
источник
Вот эквивалент 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) }
источник
В iOS 7 появился новый метод MKMapView, который вы можете использовать
источник
Я знаю, что это старый вопрос, но если вы хотите отобразить все аннотации УЖЕ НА карте, используйте это:
mapView.showAnnotations(mapView.annotations, animated: true)
источник
Одним из возможных решений может быть измерение расстояния между текущим местоположением и всеми аннотациями и использование метода MKCoordinateRegionMakeWithDistance для создания области, которая имеет немного большее расстояние, чем самая дальняя аннотация.
Это, конечно, будет медленнее, чем больше вы добавите аннотаций.
источник
- (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]; }
источник
На основе отличного ответа
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) }
источник
Добавлено небольшое условие 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 }
источник
этот код работает для меня, он показывает все контакты с текущим местоположением, надеюсь, это поможет вам,
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) }
источник
Добавление дополнительного к ответу Стефана де Луки . Здесь мы можем сохранить 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 }
источник
Надеюсь, это по крайней мере актуально, это то, что я собрал для 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); }
источник
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];
источник
На основании ответа 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
источник
Поскольку я не могу прокомментировать ответ, я хотел бы добавить немного удобства в ответ @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];
Надеюсь поможет =)
источник
Рассмотрим это расширение:
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 } }
источник
Быстрая версия 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) }
источник