0

I want to format, in real time, the number entered into a UITextField. Depending on the field, the number may be an integer or a double, may be positive or negative.

Integers are easy (see below).

Doubles should be displayed exactly as the user enters with three possible exceptions:

  • If the user begins with a decimal separator, or a negative sign followed by a decimal separator, insert a leading zero:
    • "." becomes "0."
    • "-." becomes "-0."
  • Remove any "excess" leading zeros if the user deletes a decimal point:
    • If the number is "0.00023" and the decimal point is deleted, the number should become "23".
  • Do not allow a leading zero if the next character is not a decimal separator:
    • "03" becomes "3".

Long story short, one and only one leading zero, no trailing zeros.

It seemed like the easiest idea was to convert the (already validated) string to a number then use format specifiers. I've scoured:

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html

and

http://www.cplusplus.com/reference/cstdio/printf/

and others but can't figure out how to format a double so that it does not add a decimal when there are no digits after it, or any trailing zeros. For example:

x = 23.0
print (String(format: "%f", x))
//output is 23.000000
//I want 23

x = 23.45
print (String(format: "%f", x))
//output is 23.450000
//I want 23.45

On How to create a string with format?, I found this gem:

var str = "\(INT_VALUE) , \(FLOAT_VALUE) , \(DOUBLE_VALUE), \(STRING_VALUE)"
print(str)

It works perfectly for integers (why I said integers are easy above), but for doubles it appends a ".0" onto the first character the user enters. (It does work perfectly in Playground, but not my program (why???).

Will I have to resort to counting the number of digits before and after the decimal separator and inserting them into a format specifier? (And if so, how do I count those? I know how to create the format specifier.) Or is there a really simple way or a quick fix to use that one-liner above?

Thanks!

Greg
  • 667
  • 8
  • 19
  • 1
    This is what `NumberFormatter` is for with the added bonus of ensuring it handles numbers in a locale friendly way. – rmaddy Apr 08 '18 at 20:58
  • Could you elaborate? I have figured out some of it: func convertDoubleToString(number: Double) -> String { let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.locale = Locale.current formatter.minimumSignificantDigits = 0 formatter.maximumSignificantDigits = 10 return formatter.string(from: NSNumber(value: number))! } myString = 123.45, it works. textfield.text = convertDoubleToString(number: myString) This works but if myString = 123., I want "123.". – Greg Apr 09 '18 at 02:14
  • Could you also help me in formatting comments, please? – Greg Apr 09 '18 at 02:14
  • Could you elaborate? I have figured out some of it: `func convertDoubleToString(number: Double) -> String { let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.locale = Locale.current formatter.minimumSignificantDigits = 0 formatter.maximumSignificantDigits = 10 return formatter.string(from: NSNumber(value: number))! }` myString = 123.45, it works. textfield.text = convertDoubleToString(number: myString) This works but if myString = 123., I want "123.". – Greg Apr 09 '18 at 02:23
  • slowly getting there... how to enter newlines? – Greg Apr 09 '18 at 02:26
  • Add code to you original question instead, it’s the only way to have it nicely formatted. There is a number format component you can connect to your text field in the storyboard or in code . Much simpler solution than inventing the wheel again. [NumberFormatter](https://developer.apple.com/documentation/foundation/numberformatter) – Joakim Danielson Apr 09 '18 at 05:31

1 Answers1

0

Turned out to be simple without using NumberFormatter (which I'm not so sure would really have accomplished what I want without a LOT more work).

let decimalSeparator = NSLocale.current.decimalSeparator! as String

var tempStr: String = textField.text
var i: Int = tempStr.count

//remove leading zeros for positive numbers (integer or real)
if i > 1 {
    while (tempStr[0] == "0" && tempStr[1] != decimalSeparator[0] ) {
        tempStr.remove(at: tempStr.startIndex)
        i = i - 1
        if i < 2 {
            break
        }
    }
}

//remove leading zeros for negative numbers (integer or real)
if i > 2 {
    while (tempStr[0] == "-" && tempStr[1] == "0") && tempStr[2] != decimalSeparator[0]  {
        tempStr.remove(at: tempStr.index(tempStr.startIndex, offsetBy: 1))
        i = i - 1
        if i < 3 {
            break
        }
    }
}

Using the following extension to subscript the string:

extension String {
    subscript (i: Int) -> Character {
        return self[index(startIndex, offsetBy: i)]
    }
}
Greg
  • 667
  • 8
  • 19