我试图始终将地球的特定区域保持在屏幕上。我环顾四周,发现有点工作和排序工作的代码,但没有一个真正工作......所以我编写了自己的代码,它比我发现的更好,除了一个关闭的行为。
将此方法拖放到 MKMapViewController 中将使显示区域保持在特定的缩放范围内。如果区域/缩放组合小于显示,即它在屏幕上完全可见,那么这将按预期工作。
如果区域/缩放组合大于屏幕,则可以正常工作。我检测到越界并移动地图,但似乎我的偏移量被翻转了?我得到了地图来回或上下“反弹”的引文。
我似乎无法弄清楚是什么导致弹跳或如何阻止它。
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
// Keep map in the zoom range we want
int zoom = self.map.zoomLevel;
if (self.map.zoomLevelFine > (float)self.maxZoom && self.maxZoom > 0)
{
zoom = self.maxZoom;
[self.map setCenterCoordinate:self.map.centerCoordinate zoomLevel:self.maxZoom animated:YES];
}
else if (self.map.zoomLevelFine < (float)self.minZoom && self.maxZoom > 0)
{
zoom = self.minZoom;
[self.map setCenterCoordinate:self.map.centerCoordinate zoomLevel:self.minZoom animated:YES];
}
// Figure out the current on screen boundry
MKCoordinateSpan span = self.map.region.span;
double north = self.map.centerCoordinate.latitude + (span.latitudeDelta / 2);
double south = self.map.centerCoordinate.latitude - (span.latitudeDelta / 2);
double east = self.map.centerCoordinate.longitude + (span.longitudeDelta / 2);
double west = self.map.centerCoordinate.longitude - (span.longitudeDelta / 2);
// get the offsets
float offsetLong = 0.0;
float offsetLat = 0.0;
if (self.topLeftLong < west && self.btmRightLong < east)
{
offsetLong = west - self.topLeftLong;
}
else if (self.topLeftLong > west && self.btmRightLong > east)
{
offsetLong = east - self.btmRightLong;
}
if (self.topLeftLat > north && self.btmRightLat > south)
{
offsetLat = north - self.topLeftLat;
}
else if (self.topLeftLat < north && self.btmRightLat < south)
{
offsetLat = south - self.btmRightLat;
}
// Keep the map on screen
if (offsetLat != 0.0 || offsetLong != 0.0)
{
[self.map setCenterCoordinate:CLLocationCoordinate2DMake(self.map.centerCoordinate.latitude - offsetLat,
self.map.centerCoordinate.longitude - offsetLong)
zoomLevel:zoom animated:YES];
}
// Debug
NSLog(@"Center: %f, %f, Zoom: %f", self.map.centerCoordinate.latitude, self.map.centerCoordinate.longitude, self.map.zoomLevelFine);
NSLog(@"A north: %f west: %f south: %f east: %f", north, west, south, east);
NSLog(@"B north: %f west: %f south: %f east: %f", self.topLeftLat, self.topLeftLong, self.btmRightLat, self.btmRightLong);
NSLog(@"offset %f, %f", offsetLong, offsetLat);
}
将此片段添加到 init 方法中将设置地图以在屏幕上显示 Oahu 岛。
// Get some basic setings
self.topLeftLat = 21.714852;
self.topLeftLong = -158.286896;
self.btmRightLat = 21.245862;
self.btmRightLong = -157.646942;
self.minZoom = 9.0;
self.maxZoom = 13.0;
上述方法还使用类别来帮助操作地图。
MKMapView+Extra.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
@interface MKMapView (Extra)
#pragma mark -
#pragma mark Zoom methods
- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
zoomLevel:(NSUInteger)zoomLevel
animated:(BOOL)animated;
- (int) zoomLevel;
- (float) zoomLevelFine;
#pragma mark -
#pragma mark Region methods
- (void)setRegionTopLeft:(CLLocationCoordinate2D)topLeft
bottomRight:(CLLocationCoordinate2D)bottomRight
animated:(BOOL)animated;
- (CLLocationCoordinate2D)centerPointFromRegionTopLeft:(CLLocationCoordinate2D)topLeft
bottomRight:(CLLocationCoordinate2D)bottomRight;
- (MKCoordinateSpan)spanFromRegionTopLeft:(CLLocationCoordinate2D)topLeft
bottomRight:(CLLocationCoordinate2D)bottomRight;
@end
MKMapView+Extra.m
#import "MKMapView+Extra.h"
#define MERCATOR_OFFSET 268435456
#define MERCATOR_RADIUS 85445659.44705395
@implementation MKMapView (ZoomLevel)
#pragma mark -
#pragma mark Map conversion methods
- (double)longitudeToPixelSpaceX:(double)longitude
{
return round(MERCATOR_OFFSET + MERCATOR_RADIUS * longitude * M_PI / 180.0);
}
- (double)latitudeToPixelSpaceY:(double)latitude
{
return round(MERCATOR_OFFSET - MERCATOR_RADIUS * logf((1 + sinf(latitude * M_PI / 180.0)) / (1 - sinf(latitude * M_PI / 180.0))) / 2.0);
}
- (double)pixelSpaceXToLongitude:(double)pixelX
{
return ((round(pixelX) - MERCATOR_OFFSET) / MERCATOR_RADIUS) * 180.0 / M_PI;
}
- (double)pixelSpaceYToLatitude:(double)pixelY
{
return (M_PI / 2.0 - 2.0 * atan(exp((round(pixelY) - MERCATOR_OFFSET) / MERCATOR_RADIUS))) * 180.0 / M_PI;
}
#pragma mark -
#pragma mark Helper methods
- (MKCoordinateSpan)coordinateSpanWithMapView:(MKMapView *)mapView
centerCoordinate:(CLLocationCoordinate2D)centerCoordinate
andZoomLevel:(NSUInteger)zoomLevel
{
// convert center coordiate to pixel space
double centerPixelX = [self longitudeToPixelSpaceX:centerCoordinate.longitude];
double centerPixelY = [self latitudeToPixelSpaceY:centerCoordinate.latitude];
// determine the scale value from the zoom level
NSInteger zoomExponent = 20 - zoomLevel;
double zoomScale = pow(2, zoomExponent);
// scale the map’s size in pixel space
CGSize mapSizeInPixels = mapView.bounds.size;
double scaledMapWidth = mapSizeInPixels.width * zoomScale;
double scaledMapHeight = mapSizeInPixels.height * zoomScale;
// figure out the position of the top-left pixel
double topLeftPixelX = centerPixelX - (scaledMapWidth / 2);
double topLeftPixelY = centerPixelY - (scaledMapHeight / 2);
// find delta between left and right longitudes
CLLocationDegrees minLng = [self pixelSpaceXToLongitude:topLeftPixelX];
CLLocationDegrees maxLng = [self pixelSpaceXToLongitude:topLeftPixelX + scaledMapWidth];
CLLocationDegrees longitudeDelta = maxLng - minLng;
// find delta between top and bottom latitudes
CLLocationDegrees minLat = [self pixelSpaceYToLatitude:topLeftPixelY];
CLLocationDegrees maxLat = [self pixelSpaceYToLatitude:topLeftPixelY + scaledMapHeight];
CLLocationDegrees latitudeDelta = -1 * (maxLat - minLat);
// create and return the lat/lng span
MKCoordinateSpan span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta);
return span;
}
#pragma mark -
#pragma mark Public zoom methods
- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
zoomLevel:(NSUInteger)zoomLevel
animated:(BOOL)animated
{
NSLog(@"setCenterCoordinate: zoomLevel:%d animated:", zoomLevel);
// clamp large numbers to 28
zoomLevel = MIN(zoomLevel-1, 28);
// use the zoom level to compute the region
MKCoordinateSpan span = [self coordinateSpanWithMapView:self centerCoordinate:centerCoordinate andZoomLevel:zoomLevel];
MKCoordinateRegion region = MKCoordinateRegionMake(centerCoordinate, span);
// set the region like normal
[self setRegion:region animated:animated];
}
- (int) zoomLevel
{
return 21 - round(log2(self.region.span.longitudeDelta * MERCATOR_RADIUS * M_PI / (180.0 * self.bounds.size.width)));
}
- (float) zoomLevelFine
{
return 21.0 - log2(self.region.span.longitudeDelta * MERCATOR_RADIUS * M_PI / (180.0 * self.bounds.size.width));
}
#pragma mark -
#pragma mark Public region methods
- (void)setRegionTopLeft:(CLLocationCoordinate2D)topLeft
bottomRight:(CLLocationCoordinate2D)bottomRight
animated:(BOOL)animated
{
CLLocationCoordinate2D center = [self centerPointFromRegionTopLeft:topLeft bottomRight:bottomRight];
MKCoordinateSpan span = [self spanFromRegionTopLeft:topLeft bottomRight:bottomRight];
MKCoordinateRegion region = MKCoordinateRegionMake(center, span);
// set the region like normal
[self setRegion:region animated:animated];
}
- (CLLocationCoordinate2D)centerPointFromRegionTopLeft:(CLLocationCoordinate2D)topLeft
bottomRight:(CLLocationCoordinate2D)bottomRight
{
CLLocationCoordinate2D centerPoint;
centerPoint.latitude = ((topLeft.latitude + bottomRight.latitude) / 2);
centerPoint.longitude = ((topLeft.longitude + bottomRight.longitude) / 2);
return centerPoint;
}
- (MKCoordinateSpan)spanFromRegionTopLeft:(CLLocationCoordinate2D)topLeft
bottomRight:(CLLocationCoordinate2D)bottomRight
{
MKCoordinateSpan span;
span.latitudeDelta = fabs(bottomRight.latitude - topLeft.latitude);
span.longitudeDelta = fabs(bottomRight.longitude - topLeft.longitude);
return span;
}
@end