NSLengthFormatter get stringFromMeters: только в милях/километрах

Я использую класс NSLengthFormatter для отформатируйте расстояние между пользователем и некоторым пунктом назначения.

CLLocation *userLocation; //<- Coordinates fetched from CLLocationManager
CLLocation *targetLocation; //<- Some location retrieved from server data

CLLocationDistance distance = [userLocation distanceFromLocation:targetLocation];

NSLengthFormatter *lengthFormatter = [NSLengthFormatter new];
NSString *formattedLength = [lengthFormatter stringFromMeters:distance];

Теперь, если длина меньше 1000 метров, форматированное расстояние всегда отображается в ярдах или метрах (в зависимости от локали).

Например. если расстояние = 450,0, отформатированная строка будет 492,7 ярда или 450 м.

Как я могу настроить NSLengthFormatter, чтобы возвращать строки расстояния только в милях/километрах?


person ZeMoon    schedule 20.06.2015    source источник
comment
вы пытались установить другой numberFormatter и поиграть с различными свойствами NSNumberFormatter   -  person luk2302    schedule 20.06.2015
comment
Да, я сделал. Я пытался установить количество цифр дроби и режимы округления, ничего не повлияло на результат измерения. Я все еще просматриваю свойства, но надеялся, что кто-то уже добился того же.   -  person ZeMoon    schedule 20.06.2015
comment
См. этот ответ.   -  person Droppy    schedule 20.06.2015
comment
@Droppy Спасибо, это в основном то, что я хотел (и мне придется реализовать). Но ради вопроса, как использовать NSLengthFormatter, новый класс, доступный в iOS 8, для достижения того же результата?   -  person ZeMoon    schedule 20.06.2015
comment
Я понятия не имею, но все, что вам нужно, это отформатировать ‹число›‹единица›, и это можно легко сделать с помощью NSNumberFormatter.   -  person Droppy    schedule 20.06.2015


Ответы (3)


Это то, что я в итоге использовал:

-(NSString *)formattedDistanceForMeters:(CLLocationDistance)distance
 {
    NSLengthFormatter *lengthFormatter = [NSLengthFormatter new];
    [lengthFormatter.numberFormatter setMaximumFractionDigits:1];

    if ([[[NSLocale currentLocale] objectForKey:NSLocaleUsesMetricSystem] boolValue])
    {
        return [lengthFormatter stringFromValue:distance / 1000 unit:NSLengthFormatterUnitKilometer];
    }
    else
    {
        return [lengthFormatter stringFromValue:distance / 1609.34 unit:NSLengthFormatterUnitMile];
    }
}

РЕДАКТИРОВАТЬ:

То же самое в Swift будет выглядеть так:

func formattedDistanceForMeters(distance:CLLocationDistance) -> String {
        let lengthFormatter:NSLengthFormatter! = NSLengthFormatter()
        lengthFormatter.numberFormatter.maximumFractionDigits = 1

        if NSLocale.currentLocale().objectForKey(NSLocaleUsesMetricSystem).boolValue()
        {
            return lengthFormatter.stringFromValue(distance / 1000, unit:NSLengthFormatterUnitKilometer)
        }
        else
        {
            return lengthFormatter.stringFromValue(distance / 1609.34, unit:NSLengthFormatterUnitMile)
        }
    }
person ZeMoon    schedule 20.06.2015
comment
Хорошо, спасибо. Я со всем уважением не согласен с Султаном — в картографическом приложении, я думаю, вы действительно хотите придерживаться одной единицы, чтобы пользователям не приходилось выполнять преобразование в своей голове. Этот ресторан находится в 20 милях отсюда или в 0.3 милях — никогда не показывайте футы или ярды, большое спасибо. :) - person SimplGy; 13.04.2016

Кажется, нет способа отказаться от такого поведения. Честно говоря, ваше требование не очень распространено с точки зрения UX.

Обратите внимание, что meter является базовой единицей, а не kilometer (тысяча метров). Обычно отображение 10 meters предпочтительнее отображения 0.01 kilometers. Это просто более удобно для пользователей.

На самом деле было бы очень сложно разработать API, который бы реализовывал конкретную единицу, учитывая, что базовая единица зависит от текущей локали.

Вы можете применить конкретную единицу, используя:

- (NSString *)unitStringFromValue:(double)value unit:(NSLengthFormatterUnit)unit;

но вам придется самостоятельно обрабатывать локаль, масштабирование и преобразование единиц (см. Objective c string средство форматирования расстояний)

person Sulthan    schedule 20.06.2015
comment
Это то, что я сделал. Проверьте ответ. Вы правы, это требование не очень распространено. Однако моему клиенту не нравится, когда расстояние отображается в ярдах. В странах с имперской системой, я уверен, люди без проблем видят расстояние, отформатированное в метрах. - person ZeMoon; 20.06.2015
comment
@ZeMoon Вы были бы удивлены, узнав, что многие люди в США вообще не понимают метрическую систему :) - person Sulthan; 20.06.2015
comment
Расстояния на карте, а также в дороге всегда указываются в км (при использовании метрической системы), так что на самом деле это очень распространенное требование. - person lenooh; 11.12.2016
comment
@lenooh Неправда. Я живу в стране с метрической системой и расстояния в метрах везде на дороге и в навигации по картам. Когда расстояние достаточно мало, обычно используют меньшую единицу измерения. - person Sulthan; 11.12.2016
comment
На самом деле это очень распространенное требование для людей, не использующих метрическую систему (да, я знаю...). В метрической системе имеет смысл перейти от километров к метрам, но если вы будете следовать той же логике, вы перейдете от миль к ярдам и футам. Обычно вы не хотите использовать Ярды для дорожных расстояний, и вы хотите отображать не 5000 футов, а 0,9 мили. - person Ludovic Landry; 12.11.2018

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

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

Обычно вы не хотите использовать Yards для дорожных расстояний, например, и вы хотите отображать не 5000 футов, а 0,9 мили (на самом деле Карты Google отображают в футах до 0,1 мили или 528 футов, а затем в милях).

let distanceAsDouble = 415.0

let lengthFormatter = LengthFormatter()

if Locale.current.usesMetricSystem {
    return distanceFormatter.string(fromMeters: distanceAsDouble)
} else {
    let metersInOneMile = Measurement<UnitLength>(value: 1.0, unit: .miles).converted(to: .meters).value
    if distanceAsDouble < metersInOneMile / 10 { // Display feets from 0 to 0.1 mi, then miles
        let distanceInFeets = Measurement<UnitLength>(value: distanceAsDouble, unit: .meters).converted(to: .feet).value
        return distanceFormatter.string(fromValue: distanceInFeets, unit: .foot)
    } else {
        return distanceFormatter.string(fromValue: distanceAsDouble / metersInOneMile, unit: .mile)
    }
}
person Ludovic Landry    schedule 11.11.2018