Search code examples
swiftstring-formattingnumber-formatting

NumberFormatter() - pad with a specific number of spaces possible?


I am trying to use NumberFormatter() to create a string representation of CGFloats. Ideally, I would like to create a signed string like transforming a value of

10.1 -> " 10.1" and

-10.1 -> "- 10.1", the integer part being padded with spaces.

This is the closest I have gotten, but it pads the integer portion with "0" so I get

10.1 -> " 010.1" and

-10.1 to "-010.1"

If I don't use .minimumIntegerDigits there is no padding.

I'm using a monospaced font to display the values and I'm trying to make sure they don't get jiggly as they change, and I was just hoping there would be some way to use a space in place of the 0 but otherwise exactly how .minimumIntegerDigits works.

extension CGFloat {
   func signedPaddedString() -> String {
      let formatter = NumberFormatter()
      formatter.positivePrefix = " "
      formatter.minimumIntegerDigits = 3
      formatter.paddingCharacter = " "
      formatter.minimumFractionDigits = 1
      formatter.maximumFractionDigits = 1
      return formatter.string(from: self as NSNumber)!
   }
}

Solution

  • You can achieve this by setting the positiveFormat and negativeFormat properties, which are format patterns according to the Unicode Technical Standard #35:

    extension FloatingPoint {
        func signedPaddedString() -> String {
            let formatter = NumberFormatter()
            formatter.positiveFormat = " * ##0.0"
            formatter.negativeFormat = "-* ##0.0"
            return formatter.string(for: self)!
        }
    }
    
    print((10.1).signedPaddedString())    // "  10.1"
    print((-10.1).signedPaddedString())   // "- 10.1"
    print((123.45).signedPaddedString())  // " 123.4"
    print((-123.45).signedPaddedString()) // "-123.4"
    print((0).signedPaddedString())       // "   0.0"
    print((-1).signedPaddedString())      // "-  1.0"
    

    If you want to suppress localization (e.g. in countries using decimal comma) then add

    formatter.locale = Locale(identifier: "en_US_POSIX")