MKMapView показывает некорректно сохраненный регион

Я сохраняю регион карты в пользовательские настройки по умолчанию, когда мое приложение для 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

Кто-нибудь знает, почему это происходит?

9 ответов

Проблема здесь в том, что когда вы устанавливаете регион, уровень масштабирования карты "привязывается" к ближайшему порогу масштабирования. (Я подозреваю, что эти пороги увеличения представляют собой величины увеличения, которые вы получаете при двойном или двух нажатиях)

Так, если карта показывает уровень масштабирования 1, например, и вы устанавливаете регион на то же самое значение диапазона таким образом: region = [mapView region]; [mapView setRegion:region]; он будет "привязан" к ближайшему уровню масштабирования выше уровня 1, то есть к уровню 2, и вы уменьшите масштаб примерно в два раза.

Обходной путь для оригинального плаката заключается в том, чтобы немного уменьшить значения диапазона перед установкой региона, чтобы при выходе из вида оно совпадало с уровнем масштабирования, на котором он был, а не с показанным выше.

например

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

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

Если пользователь масштабировал с двойным касанием (и, следовательно, прыгает от порога к порогу), это работает довольно хорошо, возвращая их к тому же виду почти точно.

Однако, если они сжимают-увеличивают и вид находится на полпути между пороговыми значениями увеличения, он все равно переходит на следующий уровень. Не очень хорошо в этом случае, но пока нет исправления.

Для этого на Apple Radar есть открытые ошибки, надеюсь, это будет исправлено в следующем выпуске.

У меня такая же проблема. Это очень расстраивает. Кажется, что документация для 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;

Попробуйте также хранить отношение как

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

и когда вы настраиваете свою карту

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

Это будет установлено для того же региона, той же камеры. В вашем случае вы устанавливаете только регион, но не камеру.

Итак, я уже некоторое время борюсь с этой проблемой и думаю, что нашел эффективный обходной путь, основанный на теории Круфти ( MKMapView показывает некорректно сохраненный регион). По сути, если вы установите область просмотра карты со значениями, которые вы извлекаете из NSUserDefaults после того, как представление карты выполнит свою первоначальную загрузку (в комплекте с поведением "выхватывания"), область карты будет такой, какой вы ожидаете, что она будет, Хитрость заключается в том, чтобы найти хук в коде вашего приложения где-то вниз по течению от вида карты, который был инициализирован. Не красиво, но у меня это прекрасно работает. Спасибо Crufty за понимание.

Я не могу проверить, что это работает. Кто-нибудь еще может сделать это? Я был бы готов поспорить, что то, что вы видите, является эффектом усечения ваших двойников в поплавки. В некоторых случаях, когда это происходит со мной, я обнаружил, что могу умножить диапазон на 0,9999 и сбросить регион, и тогда это будет то, что я хочу. Но не во всех регионах - иногда это сильно отличается, до 20% отличается, особенно для небольших пролетов.

Я добился большего успеха, используя коэффициент умножения 0,9 вместо 0,999, который все еще иногда выталкивает, но, кажется, обнимает ближе к желаемому увеличению. Все равно хотел бы решить эту проблему просто сказать, что я победил! ха-ха

Недавно я ударил то же самое. Используя выводы из предыдущих ответов, у меня работает следующая реализация swift 4:

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

Это устанавливает начальную область масштабирования, когда контроллер представления карты выдвигается в представление. Синхронный вызов setInitialRegion установит привязанный регион к уровню масштабирования. Асинхронный вызов setInitialRegion установит точную область. Синхронный вызов все еще необходим для удаления артефактов масштабирования.

Вот расширение 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
}

Если вы настроили MapView в InterfaceBuilder, убедитесь, что вы этого не делаете:

_mapView = [[MKMapView alloc] init];

Как только я удалил эту строку инициализации, мой вид карты внезапно начал правильно отвечать на все обновления, которые я отправил. Я подозреваю, что происходит то, что, если вы выполняете инициализацию alloc, это фактически создает другое представление, которое нигде не отображается. Тот, который вы видите на экране, является инициализированным вашим пером. Но если вы выделите init новый, тогда это будет что-то еще, и он ничего не сделает.

Другие вопросы по тегам