I'm saving map region into user defaults when my iPhone app is closing like this:
MKCoordinateRegion region = mapView.region;
[[NSUserDefaults standardUserDefaults] setDouble:region.center.latitude forKey:@"map.location.center.latitude"];
[[NSUserDefaults standardUserDefaults] setDouble:region.center.longitude forKey:@"map.location.center.longitude"];
[[NSUserDefaults standardUserDefaults] setDouble:region.span.latitudeDelta forKey:@"map.location.span.latitude"];
[[NSUserDefaults standardUserDefaults] setDouble:region.span.longitudeDelta forKey:@"map.location.span.longitude"];
When app launches again, Ш read those values back the same way, so that the user can see exactly the same map view as it was last time:
MKCoordinateRegion region;
region.center.latitude = [[NSUserDefaults standardUserDefaults] doubleForKey:@"map.location.center.latitude"];
region.center.longitude = [[NSUserDefaults standardUserDefaults] doubleForKey:@"map.location.center.longitude"];
region.span.latitudeDelta = [[NSUserDefaults standardUserDefaults] doubleForKey:@"map.location.span.latitude"];
region.span.longitudeDelta = [[NSUserDefaults standardUserDefaults] doubleForKey:@"map.location.span.longitude"];
NSLog([NSString stringWithFormat:@"Region read : %f %f %f %f", region.center.latitude, region.center.longitude, region.span.latitudeDelta, region.span.longitudeDelta]);
[mapView setRegion:region];
NSLog([NSString stringWithFormat:@"Region on map: %f %f %f %f", mapView.region.center.latitude, mapView.region.center.longitude, mapView.region.span.latitudeDelta, mapView.region.span.longitudeDelta]);
The region I read from user defaults is (not surprisingly) exactly the same as when it was saved. Notice that what is saved comes directly from the map, so it's not transformed in any way. I set it back on map with setRegion:
method, but then it is different!
Example results:
Region read : 50.241110 8.891555 0.035683 0.042915<br>
Region on map: 50.241057 8.891544 0.050499 0.054932
Does anybody know why this happens?
Here's a Swift extension to properly encode and decode MKCoordinateRegion:
This is how to use it:
The issue here is when you set the region, the map zoom level "snaps" out to the nearest zoom threshold. (I suspect these zoom thresholds are the amounts of zoom you get when you double-tap or two-finger-tap)
So if the map is showing zoom level 1 for instance, and you set the region to that same span value thusly:
region = [mapView region]; [mapView setRegion:region];
it will "snap" out to the nearest zoom level above level 1, i.e. level 2 and you will zoom out by about a factor of two.The workaround for the original poster is to reduce the span values slightly before setting the region, so that when the view snaps out, it snaps out to the zoom level it was on, not the one above.
e.g.
region.span.latitudeDelta = [[NSUserDefaults standardUserDefaults] doubleForKey:@"map.location.span.latitude"] * 0.999;
region.span.longitudeDelta = [[NSUserDefaults standardUserDefaults] doubleForKey:@"map.location.span.longitude"] * 0.999;
If the user had been zooming with double-taps (and hence jumping from threshold to threshold) this works pretty well, returning them to the same view almost exactly.
However if they pinch-zoom and the view is halfway between the zoom thresholds it will still snap out to the next level. Not so good in that case but there is no fix as yet.
There are open bugs on Apple radar for this, hopefully it will be fixed in a future release.
OK, so I've been struggling with this issue for some time now and I think that I've come up with an effective workaround inspired by Crufty's theory (MKMapView show incorrectly saved region). Basically, if you set the map view region with the values that you fetch from your NSUserDefaults after the map view has done its initial load (complete with "snap out" behavior), the map region will be what you'd expect it to be. The trick is to find a hook in your application code somewhere downstream from the map view having been initialized. Not pretty, but it works perfectly for me. Thanks to Crufty for the insight.