Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSNumberFormatter doesn't allow typing decimal numbers

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

like image 909
btschumy Avatar asked Jun 13 '11 23:06

btschumy


2 Answers

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 );
like image 150
James Bucanek Avatar answered Oct 23 '22 11:10

James Bucanek


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
                    }
                }
            }
        }
like image 23
Todd Avatar answered Oct 23 '22 09:10

Todd