We've been having trouble with the MKMapView's region in iOS7.
After our app has started and the device has changed UI orientation a couple of times, the values returned by map.region become very strange. They tend to have a sensible longitude span but a small latitude span, or vice-versa, as though the map were under the impression that its bounds had been cut down to a narrow rectangle along one edge of the screen. The actual bounds and frame of the MKMapView are still sensible after this has happened.
It's been possible to work round some of the problems this causes by calculating a region of our own from the map's actual bounds, but we still have a couple of issues that we can't fix. For example, when an annotation is tapped to bring up its callout, the map will sometimes pan to move the callout into that small part of the screen which it thinks it occupies.
Has anyone else experienced this problem ?
Our shims to implement the workaround are below, in case they are useful:
+(void)setMap: (MKMapView*) map region:(MKCoordinateRegion) region
{
CGRect realBounds = map.bounds;
MKCoordinateRegion claimedRegion = map.region; // the map's claimed region, which is wonkily different after a rotate in ios7
CGRect claimedBounds = [map convertRegion:claimedRegion toRectToView:map]; // the bounds which the map thinks its region occupies
// if we want region to map to realBounds, but the map thinks it is only claimedBounds big, what
// reduced region will map to claimedBounds ?
MKCoordinateRegion reducedRegion = [Utilities sliceRegion: region inBounds: realBounds toReducedBounds: claimedBounds];
[map setRegion:reducedRegion animated:YES];
}
+ (MKCoordinateRegion) sliceRegion: (MKCoordinateRegion) bigRegion inBounds: (CGRect) wholeBounds toReducedBounds: (CGRect) reducedBounds
{
MKCoordinateRegion reducedRegion;
// Coords of our region's corners in lat/long
CLLocationDegrees left = bigRegion.center.longitude - bigRegion.span.longitudeDelta/2.0;
CLLocationDegrees right = bigRegion.center.longitude + bigRegion.span.longitudeDelta/2.0;
CLLocationDegrees top = bigRegion.center.latitude + bigRegion.span.latitudeDelta/2.0;
CLLocationDegrees bottom = bigRegion.center.latitude - bigRegion.span.latitudeDelta/2.0;
// Coords of our bounds in pixels
CGFloat wholeLeft = wholeBounds.origin.x;
CGFloat wholeRight = wholeBounds.origin.x + wholeBounds.size.width;
CGFloat wholeTop = wholeBounds.origin.y;
CGFloat wholeBottom = wholeBounds.origin.y + wholeBounds.size.height;
// Coords of the smaller bounds in pixels
CGFloat reducedLeft = reducedBounds.origin.x;
CGFloat reducedRight = reducedBounds.origin.x + reducedBounds.size.width;
CGFloat reducedTop = reducedBounds.origin.y;
CGFloat reducedBottom = reducedBounds.origin.y + reducedBounds.size.height;
// Now work out what the lat & long values for the corners of the reduced bounds are
CLLocationDegrees newLeft = left + (right-left) * (reducedLeft - wholeLeft) / (wholeRight - wholeLeft);
CLLocationDegrees newRight = left + (right-left) * (reducedRight - wholeLeft) / (wholeRight - wholeLeft);
CLLocationDegrees newTop = top + (bottom - top) * (reducedTop - wholeTop) / (wholeBottom - wholeTop);
CLLocationDegrees newBottom = top + (bottom - top) * (reducedBottom - wholeTop) / (wholeBottom - wholeTop);
reducedRegion.center.longitude = (newRight + newLeft) / 2.0;
reducedRegion.center.latitude = (newBottom + newTop) / 2.0;
reducedRegion.span.longitudeDelta = newRight - newLeft;
reducedRegion.span.latitudeDelta = newTop - newBottom;
return reducedRegion;
}
+(MKCoordinateRegion)getMapRegion: (MKMapView*) map
{
CGRect bounds = map.bounds;
MKCoordinateRegion region = [map convertRect:bounds toRegionFromView:map]; // the region we can see on the screen, not the map's wonky region!
if ((region.span.latitudeDelta < 0.0) || (region.span.longitudeDelta < 0.0) || region.span.longitudeDelta / region.span.latitudeDelta > 5.0 || region.span.latitudeDelta / region.span.longitudeDelta > 5.0 )
{
LogD(@"getMap: region: bad span - lat: %f, long: %f", region.span.latitudeDelta, region.span.longitudeDelta);
}
return region;
}
+(void)setMap: (MKMapView*) map center: (CLLocationCoordinate2D) center
{
CGRect bounds = map.bounds;
MKCoordinateRegion boundsRegion = [map convertRect:bounds toRegionFromView:map]; // the region we can see on the screen
MKCoordinateRegion claimedRegion = map.region; // the map's claimed region, which is wonkily different after a rotate in ios7
CLLocationCoordinate2D offsetCenter; // make up a value to tell the map to center on which will make it really center
offsetCenter.latitude = center.latitude - ( boundsRegion.center.latitude - claimedRegion.center.latitude );
offsetCenter.longitude = center.longitude - ( boundsRegion.center.longitude - claimedRegion.center.longitude );
[map setCenterCoordinate:offsetCenter animated:YES];
}
+(CLLocationCoordinate2D)getMapCenter: (MKMapView*) map
{
CGRect bounds = map.bounds;
MKCoordinateRegion boundsRegion = [map convertRect:bounds toRegionFromView:map]; // the region we can see on the screen
return boundsRegion.center;
}