MKMapView显示错误保存的区域

问题描述 投票:15回答:9

当我的iPhone应用程序关闭时,我将地图区域保存为用户默认值:

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"];

当应用程序再次启动时,Ш以相同的方式读取这些值,以便用户可以看到与上次完全相同的地图视图:

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]);

我从用户默认值中读取的区域(毫不奇怪)与保存时完全相同。请注意,保存的内容直接来自地图,因此不会以任何方式进行转换。我用setRegion:方法将它设置回地图,但后来却与众不同!

示例结果:

Region read  : 50.241110 8.891555 0.035683 0.042915<br>
Region on map: 50.241057 8.891544 0.050499 0.054932

有人知道为什么会这样吗?

ios objective-c mkmapview nsuserdefaults mkcoordinateregion
9个回答
8
投票

这里的问题是当您设置区域时,地图缩放级别“捕捉”到最近的缩放阈值。 (我怀疑这些缩放阈值是双击或双指敲击时获得的缩放量)

因此,如果地图例如显示缩放级别1,并且您将区域设置为相同的跨度值:region = [mapView region]; [mapView setRegion:region];它将“捕捉”到级别1之上的最近缩放级别,即级别2,您将缩小约两倍。

原始海报的解决方法是在设置区域之前稍微减小跨度值,这样当视图突然显示时,它会跳出到它所在的缩放级别,而不是上面的缩放级别。

EG

region.span.latitudeDelta = [[NSUserDefaults standardUserDefaults] doubleForKey:@"map.location.span.latitude"] * 0.999;

region.span.longitudeDelta = [[NSUserDefaults standardUserDefaults] doubleForKey:@"map.location.span.longitude"] * 0.999;

如果用户使用双击(并因此从阈值跳到阈值)进行缩放,则效果非常好,几乎完全将它们返回到相同的视图。

但是,如果它们捏缩放并且视图位于缩放阈值之间,它仍将捕捉到下一级别。在这种情况下不太好,但目前还没有解决。

苹果雷达上存在开放式漏洞,希望它将在未来版本中修复。


2
投票

好吧,所以我一直在努力解决这个问题已经有一段时间了,我认为我已经想出了一个有效的解决方案,受到了Crufty理论(MKMapView show incorrectly saved region)的启发。基本上,如果您在地图视图完成初始加载(完成“捕捉”行为)之后使用您从NSUserDefaults获取的值设置地图视图区域,则地图区域将是您期望的地图区域。诀窍是在地图视图下游某处初始化应用程序代码中找到一个钩子。不漂亮,但它对我来说很完美。感谢Crufty的洞察力。


2
投票

我有同样的问题。这非常令人沮丧。似乎MKMapView的文档在某些与数据类型有关的方面是不正确的。

如果将区域参数设置为(double)s,您将收到错误。但是,如果传递区域参数(float),您将获得正确的行为。

所以试试吧

MKCoordinateRegion region = {{0.0f,0.0f},{0.0f,0.0f}};

region.center.latitude = (float) [[NSUserDefaults standardUserDefaults] doubleForKey:@"map.location.center.latitude"];
region.center.longitude = (float) [[NSUserDefaults standardUserDefaults] doubleForKey:@"map.location.center.longitude"];
region.span.latitudeDelta = (float) [[NSUserDefaults standardUserDefaults] doubleForKey:@"map.location.span.latitude"];
region.span.longitudeDelta = (float) [[NSUserDefaults standardUserDefaults] doubleForKey:@"map.location.span.longitude"];

mapView.region = region;

0
投票

我无法验证这是否有效。其他人可以这样做吗?我愿意打赌,你所看到的是将你的双打截断为花车的效果。在某些情况下,当我遇到这种情况时,我发现我可以将跨度乘以0.9999并重置该区域,然后它就是我想要的。但并非所有地区 - 有时它都是非常不同的,高达20%不同,特别是对于小跨度。


0
投票

我发现使用乘法因子0.9而不是0.999取得了更大的成功,它仍然有时会推出,但似乎更接近所需的缩放。仍然喜欢解决这个问题,只是说我打败了它!哈哈


0
投票

也尝试存储态度

self.lastCameraAtitude = self.mapView.camera.altitude;

当你配置你的地图

[self.mapView.camera setAltitude:self.lastCameraAtitude];

这将设置相同的区域相同的相机。在你的情况下你只设置区域而不是相机。


0
投票

这是一个Swift扩展,可以正确编码和解码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)
    }
}

这是如何使用它:

// 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
}

0
投票

最近我遇到了同样的事情。使用前面答案中的见解,以下swift 4实现对我有用:

override func viewWillAppear(_ animated: Bool) {
    if isMovingToParentViewController {
        setInitialRegion()
        DispatchQueue.main.asyncAfter(deadline: .now(), execute: {
            self.setInitialRegion()
        })
    }
}

当地图视图控制器被推入视图时,这将设置初始缩放区域。对setInitialRegion的同步调用将设置一个捕捉到缩放级别的区域。对setInitialRegion的异步调用将设置确切的区域。仍然需要同步调用来删除缩放工件。


-1
投票

如果在InterfaceBuilder中设置MapView,请确保不要这样做:

_mapView = [[MKMapView alloc] init];

一旦我删除了这个init行,我的地图视图突然开始正确响应我发送的所有更新。我怀疑发生的情况是,如果你执行alloc init,它实际上是在创建另一个没有在任何地方显示的视图。您在屏幕上看到的是您的笔尖初始化的那个。但是如果你为init分配一个新的,那么那就是其他的东西,而且它不会做任何事情。

© www.soinside.com 2019 - 2024. All rights reserved.