MKMapView show incorrectly saved region

2019-01-09 11:55发布

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?

9条回答
叼着烟拽天下
2楼-- · 2019-01-09 12:50

Here's a Swift extension to properly encode and decode MKCoordinateRegion:

extension MKCoordinateRegion {

    var encode:[String: AnyObject] {
        return ["center":
                   ["latitude": self.center.latitude,
                   "longitude": self.center.longitude],
                "span":
                   ["latitudeDelta": self.span.latitudeDelta,
                   "longitudeDelta": self.span.longitudeDelta]]
    }

    init?(decode: [String: AnyObject]) {

        guard let center = decode["center"] as? [String: AnyObject],
            let latitude = center["latitude"] as? Double,
            let longitude = center["longitude"] as? Double,
            let span = decode["span"] as? [String: AnyObject],
            let latitudeDelta = span["latitudeDelta"] as? Double,
            let longitudeDelta = span["longitudeDelta"] as? Double
        else { return nil }


        self.center = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
        self.span = MKCoordinateSpan(latitudeDelta: latitudeDelta, longitudeDelta: longitudeDelta)
    }
}

This is how to use it:

// Save
NSUserDefaults.standardUserDefaults().setObject(mapView.region.encode, forKey: "mapRegion01")

// Restore
if let dict = NSUserDefaults.standardUserDefaults().dictionaryForKey("mapRegion01"),
    let myRegion = MKCoordinateRegion(decode: dict) {

    // do something with myRegion
}
查看更多
唯我独甜
3楼-- · 2019-01-09 12:53

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.

查看更多
祖国的老花朵
4楼-- · 2019-01-09 12:54

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.

查看更多
登录 后发表回答