Средство форматирования строк Objective C для расстояний

У меня есть расстояние в виде поплавка, и я ищу способ красиво отформатировать его для читателей. В идеале я хотел бы, чтобы он менялся с m на km по мере того, как он становится больше, и хорошо округлял число. Преобразование в мили будет бонусом. Я уверен, что многие люди нуждались в одном из них, и я надеюсь, что где-то плавает какой-то код.

Вот как я хотел бы форматы:

  • 0-100 м: 47 м (в целом)
  • 100-1000 м: 325 м или 320 м (округлить до ближайших 5 или 10 метров)
  • 1000-10000 м: 1,2 км (округлить до ближайшего с одним десятичным знаком)
  • 10000m +: 21km

Если нет доступного кода, как я могу написать свой форматтер?

Спасибо


person nevan king    schedule 24.02.2010    source источник


Ответы (7)


Ни одно из этих решений не соответствовало тому, что я искал, поэтому я использовал их:

#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]);    
}
person ma11hew28    schedule 16.04.2011
comment
Мне понравилось это решение. Создал на его основе NSString+Distance-категорию. Суть: gist.github.com/1397837 - person Alex; 27.11.2011
comment
Спасибо, это как раз то, что мне нужно для CLLocationDistance. - person Alex L; 14.06.2012
comment
Какие лицензии/условия связаны с этой категорией. Я планирую использовать его в своем приложении. В моих лицензиях ВК, хотите арбитраж? - person Andrew; 15.07.2012
comment
Спасибо, мой коллега просто скопировал ваш ответ в наш проект. Я рефакторил некоторые классы, и мне было любопытно, сможет ли он написать что-то подобное. Кажется, нет! Спасибо стеку за работу для многих разработчиков. За это +1 за вашу работу, потому что я не думаю, что он проголосовал за ваш ответ. - person Alex Terente; 19.06.2013

iOS 7 и OS X 10.9 представили MKDistanceFormatter для форматирования расстояний:

Пример кода:

double myDistance = 21837.0f;

MKDistanceFormatter *df = [[MKDistanceFormatter alloc]init];
df.unitStyle = MKDistanceFormatterUnitStyleAbbreviated;

NSLog(@"myDistance is %@", [df stringFromDistance: myDistance]);

Обновление:

Кажется, что MKDistanceFormatter каким-то образом округляет входное значение. Например. когда я устанавливаю для myDistance значение 111,0, я получаю «100 м».

person Klaas    schedule 01.02.2014

Да, вам нужно написать свой собственный форматер, например

#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));
person kennytm    schedule 24.02.2010
comment
Большое спасибо за ответ, это было идеально для того, что мне было нужно (и сэкономило мне много времени). - person nevan king; 26.02.2010

NSLengthFormatter, который был представлен в iOS 8 и ОС. X 10.10 — это вариант, о котором люди должны знать.

    NSLengthFormatter *lengthFormatter = [[NSLengthFormatter alloc] init];
    lengthFormatter.unitStyle = NSFormattingUnitStyleShort;

    NSLog(@"distance = %@", [lengthFormatter stringFromMeters:meters]);

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

person ThomasW    schedule 02.09.2015
comment
Было бы неплохо, если бы он возвращал футы вместо ярдов. - person inorganik; 20.05.2017

Вот как я это делаю. При этом используется локаль пользователя для правильного форматирования строки, что, вероятно, следует сделать и вам.

// Returns a string representing the distance in the correct units.
// If distance is greater than convert max in feet or meters, 
// the distance in miles or km is returned instead
NSString* getDistString(float distance, int convertMax, BOOL includeUnit) {
  NSString * unitName;
  if (METRIC) {
    unitName = @"m";
    if (convertMax != -1 && distance > convertMax) {
      unitName = @"km";
      distance = distance / 1000;
    }
  } else {
    unitName = @"ft";
    if (convertMax != -1 && distance > convertMax) {
      unitName = @"mi";
      distance = distance / 5280;
    }
    distance = metersToFeet(distance);
  }

  if (includeUnit) return [NSString stringWithFormat:@"%@ %@", formatDecimal_1(distance), unitName];

  return formatDecimal_1(distance);

}
// returns a string if the number with one decimal place of precision
// sets the style (commas or periods) based on the locale
NSString * formatDecimal_1(float num) {
  static NSNumberFormatter *numFormatter;
  if (!numFormatter) {
    numFormatter = [[[NSNumberFormatter alloc] init] retain];
    [numFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
    [numFormatter setLocale:[NSLocale currentLocale]];
    [numFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
    [numFormatter setMaximumFractionDigits:1];
    [numFormatter setMinimumFractionDigits:1];
  }

  return [numFormatter stringFromNumber:F(num)];

}
person Andrew Johnson    schedule 24.02.2010
comment
Большое спасибо. Я не думал об использовании локализации для числовых форматов. Мне нужно время, чтобы добавить это в код, который я использовал. - person nevan king; 26.02.2010
comment
Как узнать, хочет ли пользователь METRIC или нет? - person ma11hew28; 20.09.2010
comment
Если они не живут в США, по умолчанию используется метрика. И они могут его переключать. Вы можете проверить NSLocale, чтобы узнать их местоположение. - person Andrew Johnson; 20.09.2010
comment
numFormatter = [[[NSNumberFormatter alloc] init] retain]; alloc уже выполняет сохранение. вы сохраняете слишком много раз - person user102008; 20.01.2011
comment
Распространенной ошибкой является предположение, что Великобритания использует метрическую систему. На практике большинство граждан Великобритании по-прежнему используют мили и пинты, стоуны и фунты, хотя наш бензин теперь оценивается в литрах. - person Peter Johnson; 10.05.2015

нашел это сегодня, задавая тот же вопрос .... с:

 NSString *rvalue;
    if (value > 1000) {
        rvalue = [NSString stringWithFormat:@"%.02f km",value];
    }else {
        rvalue = [NSString stringWithFormat:@"%.02f m",value];
    }

может обернуть это в метод, если это необходимо

person mlecho    schedule 18.04.2010

Для тех, кто ищет быструю версию 2.0. Перенесено с @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
  }
}
person Ciprian Rarau    schedule 18.10.2015