I am totally bewildered using NSNumberFormatter. This should be totally simple but I can't get it to work.
I'd like to set an NSTextField to allow typing decimal numbers, either with a decimal point or without. Here is what I'd think would work:
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setMinimumFractionDigits:0];
[formatter setMaximumFractionDigits:4];
[formatter setAllowsFloats:YES];
[[timeFlowMultiplierTF cell] setFormatter:formatter];
However, when typing in the textfield, pressing the "period" key for the decimal point doesn't yield one. Typing "3.14" give me "314". Throwing in [formatter setAlwaysShowsDecimalSeparator:YES] will initially format the number correctly, but if I type over it, I once again cannot type the decimal point.
What am I missing here? You would think this would be really simple
I realize this is about 4 years too late, but I just ran into this same nonsense and thought I'd share what the problem is (or could be), for posterity.
It turns out that all of the value accessors of NSTextField (-objectValue
, -stringValue
, -doubleValue
, and so on) all end up calling -validateEditing
. -validateEditing
, in turn, uses the attached NSFormatter
to convert the edited text into an object value, and then resets the text in the field with the reformatted value.
So if you have any code that watches the field as the user edits it and you "peek" at the value in the field, you are inadvertently reformatting and resetting the text in the text field.
It's not that the text field won't let you type a period; it's that is the text field already has "3" in it and when you type a period the text changes to "3.". If you then have an action/notification/delegate method that runs whenever something in the field changes, and you call any of the -typeValue methods, the "3." get formatted as "3" and it updates the cell, erasing the period you just typed.
My hack was to avoid the -typeValue methods and peek into the NSText
object to get the edited text directly, without triggering -validateEditing
:
// some method that runs every time the field changes...
NSTextField* valueField = self.valueField;
NSNumberFormatter* fieldFormatter = valueField.formatter;
NSText* fieldEditor = valueField.currentEditor;
id newValue = ( fieldEditor!=nil ? [fieldFormatter numberFromString:fieldEditor.string] : valueField.objectValue );
Thanks to and following on from @James Bucanek's answer: here is a Swift implementation that I've used when I was over-riding controlTextDidChange
delegation method which unblocked the user from typing a decimal point. It also updates the enabled
flag of a button on the interface according to if there's a valid (i.e > zero and non-zero length string) entry:
override func controlTextDidChange(notification: NSNotification) {
if let formatter: NSNumberFormatter? = self.user_textfield.formatter as? NSNumberFormatter {
if let field_editor: NSText = self.user_textfield.currentEditor() {
if let new_value: Float? = formatter!.numberFromString(field_editor.string!)?.floatValue {
self.my_button_out.enabled = new_value > 0.0
} else {
self.my_button_out.enabled = false
}
}
}
}