Subclassing NSTextField to Allow Only Numbers
Example project available on GitHub – CINumberField
Yes, I know that one may use NSNumberFormatter to achieve a similar result – but what I don’t like about about that solution is this:
If a user types non-numeric characters into the NSTextField and attempts to tab away from the field, they get the default error sound and their tab attempt is denied.
To me this isn’t user friendly.
I figured I’d take a Javascript-esque approach to the problem: when the user has finished interacting with the text field, check whether said content is numeric. If it isn’t, beat it until it learns.
Witness the incredible simplicity:
NumberField.h
// NumberField.h // Created by Michael Robinson (Code of Interest) on 28/11/10. #import <Cocoa/Cocoa.h> @interface NumberField : NSTextField { } -(void) textDidEndEditing:(NSNotification *)aNotification; @end |
NumberField.m
// NumberField.m // Created by Michael Robinson (Code of Interest) on 28/11/10. #import <NumberField.h> @implementation NumberField -(void) textDidEndEditing:(NSNotification *)aNotification { // replace content with its intValue ( or process the input's value differently ) [self setIntValue:[self intValue]]; // make sure the notification is sent back to any delegate [[self delegate] controlTextDidEndEditing:aNotification]; } @end |
To use the class, open your NIB in Interface Builder and select the NSTextField you want to limit to numbers only, open the Inspector, open the Identity tab, and paste the name of your new class in the “Class” text field.


I’m glad to see such a simple example demonstrating how this can be done. I’ve used this example to put together a field which only allows version numbers to be input; e.g., integers separated by periods.
Thanks a bunch!
I’ve found it pretty hard to find information on some aspects of Cocoa, glad you found this post helpful :)
That’s pretty awesome! There wouldn’t happen to be a similarly simple way to then format the number as a US phone number, would there?
It shouldn’t be too difficult. Get the string, do some scanning and parsing, and then put the result directly back into the box. Like this:
As for how you’re going to get the phone number out, well, we’ll leave that to you to figure out! ;-) What I’d do is remove all non-integer characters to get a string of pure digits. Then count the digits to get a guess at how long the number is going to be—remember, this will happen as the user types, so expect most iterations of the
textDidChange:method to contain incomplete numbers. Based on the guess, insert appropriate punctuation back into the string, and redisplay. This methodology is based on the fact that the dialer has nothing but numbers to put in, so if you can reduce the input to a series of digits, you can reconstruct the string the way you need it.
As a user of this field, however, the use of punctuation becomes difficult, as I would expect that
1. My input be changed as little as possible as I type.
2. Characters which are allowed in the output not be stripped from the input until a number of valid length (7, 10 or 11 digits) is detected. For instance, if I type a leading parenthesis or hanging dash, I don’t want it to disappear until it’s clear whether it’s valid or not.
Perhaps you could achieve this by allowing some flexibility in the format until the field has finished editing, whereupon you tighten the requirements and fill in the precisely-formatted number. Just a thought.
Good Luck!
thanks! this is really simple and helpful.
i’ve noticed that I can’t type in negative values…i’m unsure of how to go about doing that… any ideas?
If you type a number, then add the ‘-’ sign, it works. If you try to type the ‘-’ sign into an empty input, it won’t.
This is because textDidChange is called each time the user types – each time the user enters / removes a character, the input’s value is replaced with whatever comes out of [input intValue]. The string ‘-’ is not a number, therefore it’s integer value comes out as ’0′.
If you use textDidEndEditing instead of textDidChange, the value replacement occurs only when the user has stopped interacting with the input – thus giving the user a chance to type a negative number normally.
Thanks for noticing this – I’ve updated the example with these changes.
I would try allowing the – character in addition to 0-9 in textDidChange. Then, along with what @faceleg mentioned, use textDidEndEditing to make sure the user didn’t leave it at just a hyphen with no number, turning it instead into a zero or empty string, etc.
You could also use RegexKitLite, which would potentially simplify your searching and replacing :)
Hi,
This is really a nice article. Exactly what I was looking for. Thanks for sharing this.
Your code is giving me a compiler warning.
warning: “-controlTextDidEndEditing” not found in protocols
Do you maybe know how to solve this warning? Do I have to include something?
Also I am just wondering if
could be replaced with:
and the code will still have the same effect?
Thanks.
Hi Tomaz,
Thanks for your comment. What versions of XCode / OS X are you using?
I tried replacing
With
And got this error:
So it seems that one can’t simply replace the former with the latter, I’m not a Cocoa guru – do any ideas why this is not possible?
Hi Michael,
Sorry, my mistake. I meant this:
I am using XCode 3.2.6 on X 10.6
Thanks
Regarding
You’re right – I was having being silly and doing:
Dumb I know.
I’ve changed the code above to use your modification, and updated the GitHub repo.
Let me know if that fixed your build warning?
Hi Michael,
when calling the base class:
super textDidEndEditing:aNotification];
I am not getting the build warning any more.
Thanks.
Tomaz, another thought – did you use the code from this article, or that found in the related github repository?
The repository’s code is more up-to-date: CINumberField.
Hello Michael,
thank you really much for your very good and detailed description. I was just looking for this solution.
By the way. I’m working with Xcode 4.5.2 and
works fine for me.
Joerg
Hi Joerg,
Happy it was helpful!