I have a distance as a float and I\'m looking for a way to format it nicely for human readers. Ideally, I\'d like it to change from m to km as it gets bigger, and to round the n
None of these solutions really met what I was looking for, so I built on them:
#define METERS_TO_FEET  3.2808399
#define METERS_TO_MILES 0.000621371192
#define METERS_CUTOFF   1000
#define FEET_CUTOFF     3281
#define FEET_IN_MILES   5280
- (NSString *)stringWithDistance:(double)distance {
    BOOL isMetric = [[[NSLocale currentLocale] objectForKey:NSLocaleUsesMetricSystem] boolValue];
    NSString *format;
    if (isMetric) {
        if (distance < METERS_CUTOFF) {
            format = @"%@ metres";
        } else {
            format = @"%@ km";
            distance = distance / 1000;
        }
    } else { // assume Imperial / U.S.
        distance = distance * METERS_TO_FEET;
        if (distance < FEET_CUTOFF) {
            format = @"%@ feet";
        } else {
            format = @"%@ miles";
            distance = distance / FEET_IN_MILES;
        }
    }
    return [NSString stringWithFormat:format, [self stringWithDouble:distance]];
}
// Return a string of the number to one decimal place and with commas & periods based on the locale.
- (NSString *)stringWithDouble:(double)value {
    NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
    [numberFormatter setLocale:[NSLocale currentLocale]];
    [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
    [numberFormatter setMaximumFractionDigits:1];
    return [numberFormatter stringFromNumber:[NSNumber numberWithDouble:value]];
}
- (void)viewDidLoad {
    [super viewDidLoad];
    double distance = 5434.45;
    NSLog(@"%f meters is %@", distance, [self stringWithDistance:distance]);
    distance = 543.45;
    NSLog(@"%f meters is %@", distance, [self stringWithDistance:distance]);    
    distance = 234234.45;
    NSLog(@"%f meters is %@", distance, [self stringWithDistance:distance]);    
}
iOS 7 and OS X 10.9 introduced MKDistanceFormatter for formatting distances:
Code Example:
double myDistance = 21837.0f;
MKDistanceFormatter *df = [[MKDistanceFormatter alloc]init];
df.unitStyle = MKDistanceFormatterUnitStyleAbbreviated;
NSLog(@"myDistance is %@", [df stringFromDistance: myDistance]);
Update:
It seems that MKDistanceFormatter is rounding the input value somehow. E.g. when I set myDistance to 111.0 I get "100 m".
NSLengthFormatter which was introduced with iOS 8 and OS X 10.10 is an option people should be aware of.
    NSLengthFormatter *lengthFormatter = [[NSLengthFormatter alloc] init];
    lengthFormatter.unitStyle = NSFormattingUnitStyleShort;
    NSLog(@"distance = %@", [lengthFormatter stringFromMeters:meters]);
However, NSLengthFormatter has doesn't use Imperial units in those locales where they use metric except for distances.
Yes you need to write your own formatter, like
#include <math.h>
NSString* convertDistanceToString(float distance) {
    if (distance < 100)
       return [NSString stringWithFormat:@"%g m", roundf(distance)];
    else if (distance < 1000)
       return [NSString stringWithFormat:@"%g m", roundf(distance/5)*5];
    else if (distance < 10000)
       return [NSString stringWithFormat:@"%g km", roundf(distance/100)/10];
    else
       return [NSString stringWithFormat:@"%g km", roundf(distance/1000)];
}
...
NSLog(@"The distance is %@", convertDistanceToString(1024));
found this today asking the same question....going with :
 NSString *rvalue;
    if (value > 1000) {
        rvalue = [NSString stringWithFormat:@"%.02f km",value];
    }else {
        rvalue = [NSString stringWithFormat:@"%.02f m",value];
    }
could wrap this in a method, if need be
For those looking for a swift 2.0 version. Ported from @MattDiPasquale
 extension Double {
  func toDistanceString() -> String {
    let METERS_TO_FEET = 3.2808399
    let METERS_CUTOFF = 1000.0
    let FEET_CUTOFF = 3281.0
    let FEET_IN_MILES =  5280.0
    let format:String
    if (NSLocale.isMetric()) {
      if (self < METERS_CUTOFF) {
        format = "\(self.stringWithDecimals(0)) metres"
      } else {
        format = "\((self / 1000).stringWithDecimals(1)) km";
      }
    } else { // assume Imperial / U.S.
      let feet = self * METERS_TO_FEET;
      if (feet < FEET_CUTOFF) {
        format = "\(feet.stringWithDecimals(0)) feet";
      } else {
        format = "\((self / FEET_IN_MILES).stringWithDecimals(1)) miles";
      }
    }
    return format
  }
  func stringWithDecimals(decimals:Int) -> String {
    return String(format: "%.\(decimals)f", self)
  }
}
extension NSLocale {
  class func isMetric() -> Bool {
    let locale = NSLocale.currentLocale()
    return locale.objectForKey(NSLocaleUsesMetricSystem) as! Bool
  }
}