NSFormatter

来源:互联网 发布:电脑卸载不了软件 编辑:程序博客网 时间:2024/06/14 14:40

http://nshipster.com/nsformatter/

Conversion is the tireless errand of software development. Most programming tasks boil down to some variation of transforming data into something more useful.

In the case of user-facing software, converting data into human-readable form is an essential task, and a complex one at that. A user's preferred language, locale, calendar, or currency can all factor into how information should be displayed, as can other constraints, such as a label's dimensions.

All of this is to say that sending -description to an object just isn't going to cut it in most circumstances. Even +stringWithFormat: is going to ultimately disappoint. No, the real tool for this job is NSFormatter.


NSFormatter is an abstract class for transforming data into a textual representation. It can also interpret valid textual representations back into data.

Its origins trace back to NSCell, which is used to display information and accept user input in tables, form fields, and other views in AppKit. Much of the API design of NSFormatter reflects this.

Foundation provides two concrete subclasses for NSFormatterNSNumberFormatter andNSDateFormatter. As some of the oldest members of the Foundation framework, these classes are astonishingly well-suited to their respective domains, in that way only decade-old software can.

NSNumberFormatter

NSNumberFormatter handles every aspect of number formatting imaginable, from mathematical and scientific notation, to currencies and percentages. Nearly everything about the formatter can be customized, whether its the currency symbol, grouping separator, number of significant digits, rounding behavior, fractions, character for infinity, string representation for 0, or maximum / minimum values. It can even write out numbers in several languages!

Number Styles

When using an NSNumberFormatter, the first order of business is to determine what kind of information you're displaying. Is it a price? Is this a whole number, or should decimal values be shown?

NSNumberFormatter can be configured for any one of the following formats, with -setNumberStyle::

To illustrate the differences between each style, here is how the number 12345.6789 would be displayed for each:

  • NSNumberFormatterNoStyle: 12346
  • NSNumberFormatterDecimalStyle: 12345.6789
  • NSNumberFormatterCurrencyStyle: $12345.68
  • NSNumberFormatterPercentStyle: 1234567%
  • NSNumberFormatterScientificStyle: 1.23456789E4
  • NSNumberFormatterSpellOutStyle: twelve thousand three hundred forty-five point six seven eight nine

Locale Awareness

By default, NSNumberFormatter will format according to the current locale settings, for things like currency symbol ($, £, €, etc.) and whether to use "," or "." as the decimal separator.

NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];for (NSString *identifier in @[@"en_US", @"fr_FR"]) {    numberFormatter.locale = [NSLocale localeWithLocaleIdentifier:identifier];    NSLog(@"%@: %@", identifier, [numberFormatter stringFromNumber:@(1234.5678)]);}
en_US: $1,234.57fr_FR: 1 234,57 €

All of those settings can be overridden on an individual basis, but for most apps, the best strategy would be deferring to the locale's default settings.

Rounding & Significant Digits

In order to prevent numbers from getting annoyingly pedantic ("thirty-two point three three, repeating, of course..."), make sure get a handle on NSNumberFormatter's rounding behavior.

The easiest way to do this, would be to setUsesSignificantDigits: to YES, and then set minimum and maximum number of significant digits appropriately. For example, a number formatter used for approximate distances in directions, would do well with significant digits to the tenths place for miles or kilometers, but only the ones place for feet or meters.

For anything more advanced, an NSDecimalNumberHandler object can be passed as theroundingBehavior property of a number formatter.

NSDateFormatter

NSDateFormatter is the be all and end all of getting textual representations of both dates and times.

Date & Time Styles

The most important properties for a NSDateFormatter object is its dateStyle and timeStyle. Like NSNumberFormatter -numberStyle, these styles provide common preset configurations for common formats. In this case, the various formats are distinguished by their specificity (more specific = longer).

Both properties share a single set of enum values:

StyleDescriptionExamples DateTimeNSDateFormatterNoStyleSpecifies no style.  NSDateFormatterShortStyleSpecifies a short style, typically numeric only.11/23/373:30pmNSDateFormatterMediumStyleSpecifies a medium style, typically with abbreviated text.Nov 23, 19373:30:32pmNSDateFormatterLongStyleSpecifies a long style, typically with full text.November 23, 19373:30:32pmNSDateFormatterFullStyleSpecifies a full style with complete details.Tuesday, April 12, 1952 AD3:30:42pm PST

dateStyle and timeStyle are set independently. For example to display just the time, anNSDateFormatter would be configured with a dateStyle of NSDateFormatterNoStyle:

NSDateFormatter *formatter = [[NSDateFormatter alloc] init];[formatter setDateStyle:NSDateFormatterNoStyle];[formatter setTimeStyle:NSDateFormatterMediumStyle];NSLog(@"%@", [formatter stringFromDate:[NSDate date]]);// 12:11:19pm

Whereas setting both to NSDateFormatterLongStyle yields the following:

NSDateFormatter *formatter = [[NSDateFormatter alloc] init];[formatter setDateStyle:NSDateFormatterLongStyle];[formatter setTimeStyle:NSDateFormatterLongStyle];NSLog(@"%@", [formatter stringFromDate:[NSDate date]]);// Monday, November 11, 2013 12:11:19pm PST

As you might expect, each aspect of the date format can alternatively be configured individually, a la carte. For any aspiring time wizards NSDateFormatter has a bevy of different knobs and switches to play with.

Relative Formatting

As of iOS 4 / OS X 10.6, NSDateFormatter supports relative date formatting for certain locales with the doesRelativeDateFormatting property. Setting this to YES would format the date of[NSDate date] to "Today".

Re-Using Formatter Instances

Perhaps the most critical detail to keep in mind when using formatters is that they are extremelyexpensive to create. Even just an alloc init of an NSNumberFormatter in a tight loop is enough to bring an app to its knees.

Therefore, it's strongly recommended that formatters be created once, and re-used as much as possible.

If it's just a single method using a particular formatter, a static instance is a good strategy:

- (void)fooWithNumber:(NSNumber *)number {    static NSNumberFormatter *_numberFormatter = nil;    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        _numberFormatter = [[NSNumberFormatter alloc] init];        [_numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];    });    NSString *string = [_numberFormatter stringFromNumber:number];    // ...}

dispatch_once guarantees that the specified block is called only the first time it's encountered.

If the formatter is used across several methods in the same class, that static instance can be refactored into a singleton method:

+ (NSNumberFormatter *)numberFormatter {    static NSNumberFormatter *_numberFormatter = nil;    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        _numberFormatter = [[NSNumberFormatter alloc] init];        [_numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];    });    return _numberFormatter;}

If the same formatter is privately implemented across several classes, one could either expose it publicly in one of the classes, or implement the static singleton method in a category onNSNumberFormatter.


If your app deals in numbers or dates, NSFormatter is indespensable. Actually, if your app doesn't... then what does it do, exactly?

Presenting useful information to users is as much about content as presentation. Invest in learning all of the secrets of NSNumberFormatter and NSDateFormatter to get everything exactly how you want them.

And if you find yourself with formatting logic scattered across your app, consider creating your ownNSFormatter subclass to consolidate all of that business logic in one place.

FormatterKit has great examples of NSFormatter subclasses for addresses, arrays, colors, locations, ordinal numbers, time intervals, and units of information.