Ответ 1
Начиная с iOS7 вы можете использовать showAnnotations: анимированные:
[mapView showAnnotations:annotations animated:YES];
У меня есть несколько аннотаций, которые я хочу добавить в свой 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
?
Спасибо заранее.
Начиная с iOS7 вы можете использовать showAnnotations: анимированные:
[mapView showAnnotations:annotations animated:YES];
ссылка, отправленная Джим, теперь мертва, но я смог найти код (который я где-то помещал в закладки). Надеюсь, это поможет.
- (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];
}
Почему так сложно?
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);
}
Я сделал что-то похожее на это, чтобы уменьшить (или в) область, содержащую аннотацию точки и текущее местоположение. Вы можете расширить его, заполнив аннотации.
Основные шаги:
-(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];
}
У меня другой ответ. Я собирался реализовать алгоритм масштабирования для соответствия, но я решил, что у 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;
}
Основываясь на информации и предложениях от всех, я придумал следующее. Спасибо всем за участие в обсуждении за вклад. Это будет выглядеть в представлении Controller, содержащем 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];
}
В моем случае я начинаю с объектов 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, многоугольник и дополнительное дополнение, я использовал следующее:
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)
}
Вот эквивалент 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)
}
В "MKMapView" есть новый метод с iOS 7, который вы можете использовать
Заявление
СВИФТ
func showAnnotations(_ annotations: [AnyObject]!, animated animated: Bool)
OBJECTIVE-C
- (void)showAnnotations:(NSArray *)annotations animated:(BOOL)animated
Параметры
аннотации Аннотации, которые вы хотите видеть в карта. анимированный YES, если вы хотите изменить регион карты анимированный или НЕТ, если вы хотите, чтобы карта отображала новый регион сразу без анимации.
Обсуждение
Вызов обновления этого метода значение в свойстве области и потенциально другие свойства для отражают новую область карты.
Одним из возможных решений может быть измерение расстояния между текущим местоположением и всеми аннотациями и использование метода 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)
}
Я знаю, что это старый вопрос, но если вы хотите отобразить все аннотации, УЖЕ НА КАРТЕ, используйте это:
mapView.showAnnotations(mapView.annotations, animated: true)
Добавлен небольшой оператор if для обработки 1 location-, чтобы добавить фрагмент кода суффикса. Используется функция 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 code
}
Я надеюсь, что это по крайней мере актуально, вот что я собрал для 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 file
#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];
Я надеюсь, что это поможет =)
этот код работает для меня, он показывает все контакты с текущим местоположением, надеюсь, что это поможет вам,
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)
}
Рассмотрим это расширение:
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
}
}
Версия Swift 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)
}