I am using the NSLengthFormatter class to format the distance between the user and some destination.
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];
Now, if the length is less than 1000 meters, the formatted distance is always shown in yards or meters (depending on the locale).
Eg. if distance = 450.0, the formatted string will be 492.7 yd or 450 m.
How can I tweak NSLengthFormatter to return the distance strings in miles/kilometers only?
This is what I have ended up using:
-(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];
}
}
EDIT:
The same in Swift would look like:
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)
}
}
There doesn't seem a way to opt out of this behaviour. To be honest, your requirement is not very common from UX perspective.
Note that meter
is the base unit, not a kilometer
(a thousand of meters). Usually, displaying 10 meters
is preferred over displaying 0.01 kilometers
. It's just more friendly for the users.
It would be actually very hard to design an API that would enforce a specific unit considering that the base unit depends on current locale.
You can enforce a specific unit using:
- (NSString *)unitStringFromValue:(double)value unit:(NSLengthFormatterUnit)unit;
but you will have to handle the locale and scaling & unit conversion by yourself (see Objective c string formatter for distances)
It's actually a very common requirement for people not using metric system (Yes I know...).
In metric system it just makes sense to go from Kilometers to Meters, etc. If you follow the same logic with the imperial system you'll go from Miles to Yards to Foot.
Usually you don't want to use Yards for road distances for example and you don't want to display 5,000 ft but 0.9 mi (Actually Google Maps display in feet up to 0.1 miles or 528 feet, and then in miles).
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)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With