The Curious Case of the Missing Decimal Point Button
The curious case of the missing decimal point button on the Number Pad keyboard type is discussed in quite a few places on the interwebs. The discussion around Apple’s decision to not include a decimal point button usually goes something like this…
Newbie: “Nice one Apple. Where’s the decimal point button in the number pad? The user can’t enter any decimal numbers using the number pad!”
AcidBurnDev: “You have to use the Numbers & Punctuation keyboard type instead.”
1337_Dev: “Or you could be 1337 and add a custom decimal point button to the Number Pad.”
CrashOverrideDev: “Log an enhancement to Apple about this. I can’t believe they forgot this vital button!”
Newbie: “Great, thanks!”
For Gratuitous, we explicitly choose not to include a decimal point button on the Number Pad. Didn’t we see all those examples and forum posts telling us how to add that button? (yep) Are we just lazy? (yep…er, nope)
Let’s take a currency value of $1.23. Every other tip calculator requires the user to tap the following:
- User taps 1
- User taps a custom decimal point button
- User taps 2
- User taps 3
Count ‘em. That’s four taps and it turns out, it is one tap too many because you can simply infer it from the user’s input. Going back to this example, here’s what Gratuitous does when the user taps those same values:
- User taps 1, Gratuitous displays $0.01
- User taps 2, Gratuitous displays $0.12
- User taps 3, Gratuitous displays $1.23
Count ‘em. That’s three taps and we’ve just saved the user time. At this point everyone should be slapping their forehead and saying “of course that’s how currency input should be handled”. In fact, that’s what we said too when we saw all of the other tip calculators sticking a custom decimal point button on the number pad. Hopefully, this isn’t new to anyone (used an ATM recently?).
The above example was using USD currency locale. Now let’s imagine a currency value of ¥123 (that’s Japanese yen). Did you know that yen is only represented in whole amounts? Thus, there is no decimal value for yen. Including one in this situation does not present a very good user experience because a decimal point is unnecessary is some situations. Aside from currency, number pads can be used for various types of input (credit card numbers, social security numbers, etc) that do not require a decimal point.
At Uproar, this is part of what keeps us up at night in our goal of designing applications with an excellent user experience. Let us know how we’re doing.
(For those of you that frequent stackoverflow.com, portions of this post came from my answer to a question on this topic.)





You have any sample code? I’m having trouble getting the numberformatter to update the NSTextField as the user types.
Mike
24 Apr 09 at 6:34 pm
This post was made with iPhone development in mind so I’m using UITextField and the UITextFieldDelegate protocol. Implementing UITextFieldDelegate’s textField:shouldChangeCharactersInRange:replacementString: allows you to watch for new characters and handle the inclusion of a decimal point when necessary.
Sounds like you’re doing Cocoa development rather than iPhone development. Assuming you’re using the NSTextFieldDelegate protocol, you should be able to use controlTextDidChange: to do accomplish this.
kevin
25 Apr 09 at 7:30 am
Thanks but I try to understand this, then get sidetracked, revisit, sidetracked…
Anyway, I still can’t get my head around this shouldChangeCharactersInRange:: delegate.
Assuming I have set up an NSNumberFormatter as so:
numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
Upon first display of the text field it displays “$0.00″
this fires the delegate method with these results:
NSLog(@”number in text field: %@”, [numberFormatter numberFromString:textField.text])
console: 0
NSLog(@”Range Location: %u, Length: %u”, range.location, range.length);
console: Range Location: 5, Length: 0
NSLog(@”String: %@”, string);
console: String: 1
I guess I’m not familiar enough with Cocoa (basic mathematics?!?) to sort out how to change the textField.text appropriately without using brute force along the lines of: if number = 0 then return YES. If number <.1 {number = number * 10 + string * .01}, etc. etc.
obviously, that’s not valid syntax…nor is does it make much sense.
What am I missing?
Mike
12 May 09 at 10:46 pm
I seem to have sorted this out with about 6 lines of code based around utilizing NSDecimalNumber’s decimalNumberByMultiplyingByPowerOf10 within UITextField’s shouldChangeCharactersInRange delegate method.
Still be interested in seeing how you handled it as I’m doing conversions from NSString to NSDecimalNumber with and back NSNumberFormatters involved upon ever single “key” press from the user. but, it’s working!
Mike
14 May 09 at 1:25 pm
Glad to hear you got this working in your own code, Mike. It sounds like you handled it the same way we are. We’ve got a singleton utility class that has a single instance of a formatter that is used to do the conversions.
kevin
15 May 09 at 10:07 am
Hi
I saw you comment on my question about the keyboard on stackoverflow. Unfortunately in my case I am not dealing with fixed decimal points so I really do need a decimal point.
Your solution here would only work in certain situations.
John Smith
23 Aug 09 at 12:20 pm
Nice idea.
Of course, to enter $123.00, it requires two extra taps, like an ATM machine. And as John Smith noted, this only works for the familiar currency situation.
There’s no excuse for Apple not to have a decimal point option on a numeric keypad! Or a public API facility for building custom keypads.
Bruce LeSourd
15 Sep 09 at 4:12 pm
@Bruce LeSourd
Yep, this is only viable for currency and other values where the decimal can be inferred. For instance, this would also work for measurements with fixed precisions like medical dosages (never more than 2 or 4 decimal places).
And yes, you are right in that it does require 2 extra taps for currency values ending in .00. However, this only occurs 1% of the time.
So, as compared to a decimal button design, this design…
1% of the time requires 2 extra taps (whole amounts; eg $123.00)
9% of the time requires the same number of taps (amounts ending in multiples of 10 cents; eg, $123.10)
90% of the time saves 1 tap (all other amounts; eg $123.45)
My guess as to why Apple doesn’t provide a stock keypad with a decimal point is that it is not that common of a use-case. If you use the design outlined in this post you will hit most of the use cases where people are asking for a decimal button today. In other words, the number of apps today that legitimately require just 0-9, decimal, and backspace buttons are very few and not worth introducing another NumericKeypad option to include the decimal button.
As for Apple offering a custom keypad API, I don’t see the need. Additionally, I think creating a first-class, generic UI API to construct a keypad would be difficult. With the 1.2 update to Gratuitous, I rolled my own keypad (details in a future blog post) and it was surprisingly simple.
Thanks for the feedback and your thoughts on this post, I really appreciate it!
kevin
15 Sep 09 at 7:13 pm
I would love if someone could post a shouldChangeCharactersInRange solution for this with localized formatting!
Erik
5 Nov 09 at 2:40 am
Thanks for the great idea on currency input. I second Erik’s request for an example of using shouldChangeCharactersInRange. I don’t quite get how you use that delegate method and the currency formatter together.
Jeff
22 Dec 09 at 12:25 am
This is how I did it. I’m not sure it’s the best way, but it works:
@property (nonatomic, retain) NSString *tempStore;
———
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if ([string length] > 0) { //NOT A BACK SPACE Add it
self.tempStore = [NSString stringWithFormat:@"%@%@", self.tempStore, string];
} else {
//Back Space do manual backspace
if ([self.tempStore length] > 1) {
self.tempStore = [self.tempStore substringWithRange:NSMakeRange(0, [self.tempStore length]-1)];
} else {self.tempStore = @”0″;}
}
// Convert to decimal place
float tempDouble = [self.tempStore floatValue];
tempDouble = tempDouble / 100;
textField.text = [NSString stringWithFormat:@"$%.2f", tempDouble];
return NO;
}
Jordanc
1 Feb 10 at 1:43 pm
Jordanc, it’s works perfectly!! Thanks!
wal
9 Apr 10 at 8:17 pm
I have been looking through your example. when i try to run it i get 1 error on this line: @property (nonatomic, retain) NSString *tempStore;
the error is: Property declaration not in @interface or @implementation context.
I’m fairly new to -c and not sure how to fix this error.
Tom
19 Apr 10 at 2:23 am