restricting character input in nstextfield

hi all

total newbie question here (learning cocoa and objective-c from books and tutorials):

what is the easiest way to restrict input in an nstextfield object? i've got an interface working very well with the controller i wrote, but i would like the user to be able to enter only numbers (0-9) in a text field. i could do the check in the program and send an error message, but i would like the interface to simply block any character input which is not a number.

thanks.

Posted on Dec 21, 2007 5:27 AM

Reply
13 replies

Dec 21, 2007 9:14 AM in response to Nawyecky

There is two easier more straightforward ways which will give you something close to what you're looking for and then there's the real way which would mean digging into the Cocoa internals more than you might want to at this point.

NSTextField has two methods that are sent to any object designated as its delegate. (If you don't know about delegates yet read up on those.) The first is textDidEndEditing which is invoked when the user exits the TextField itself. At this point you can examine the text that was entered, remove any non-numbers, beep put up an error message etc. You can do the same thing in more minute detail using the delegate method textDidChange where you should be able to examine the contents each time the text in the method changes. Attacking at this level should give you the ability to remove non-numbers as they are entered.

This second method is probably as close as you can get to what (I think) you want without getting pretty complicated. To test delegate methods set some up and just put an NSLog or beep command in them and then you can see when they are invoked as you edit the TextField.

If you really want to interdict the key as it's typed you're going to have to dig into the NSResponder chain and the NSView that the NSTextFlield inherits from. While learning the NSResponder chain is great exercise for any Cocoa programmer it is not simple. See if if using either of the pre-rolled NSTextField delegate methods will work and if not then prepare to dive deeper.

Hope that helps,

=Tod

Dec 21, 2007 1:51 PM in response to Nawyecky

I think he does a good job - they're actually easier than you're probably expecting. The core functionality is in the class and the secondary functionality is assigned to a delegate. Essentially the delegate holds the code the designers found they needed when they really used a class in depth. A delegate can be any object even the one you're already working with - in fact it usually is. In IB just designate the Object you're already using as the delegate and then you can override the delegate subroutines and add your own functionality.

It demonstrates the depth of Cocoa in this process. Usually there is already a declared routine for what you want to do when you look for it. For example windowWillClose is where you check to see if the user needs to save data but windowDidClose exists too for anything you want to when the window closes and so on. Even when you decide to use a delegate you only have declare/override the routines you want even if it's just one of them.

And if you really get off the reservation you can retroactively add your own routines to an existing class - but that's a discussion for another time. Don't be worried about delegates they're really quite easy.

=Tod

Dec 21, 2007 3:17 PM in response to Tod Kuykendall

ok, after reading some stuff at cocoadev, as well as a chapter or two of the apple docs, the concept is much clearer. now when i read hillegass i can understand what he says...

unfortunately, when i try to implement a very simple delegation method, it doesn't seem to work. here's what i did:

in ib, control-drag from a textfield to my class instance (the blue cube) and choose "delegate" (i might add that the above textfield is communicating perfectly with the class, i can retrieve values as well as display them when i run the app).

then, i add the following method to that class definition:

- (void) textDidEndEditing: (NSNotification *) aNotification
{
NSLog(@"editing ended");
}

I'm not even sure what the aNotification object looks like, i just thought that the method will be called regardless. but it doesn't. what am i missing here?

thanks again.

Dec 21, 2007 4:02 PM in response to Nawyecky

- (void)

You miss the - before (void).

For delegate I suggest you copy and past method from the documentation so
you will be sure to have the exact spelling.

But why not use a number formatter in Interface Builder?
Just drag the formatter into the NSTextField and adjust the settings into the info panel.
That would make the system NSBeep if one try to validate something else that number.

Dec 21, 2007 6:21 PM in response to Nawyecky

Did you designate the object as the delegate of the NSTextField?
The easiest way to do this is to control-drag in IB from the NSTextField to the blue cube of the object. Then under Outlets choose "Delegate". (Described steps are for Xcode 2.x IB BTW.)

If you've done this then it's probably a typo in the code - either the name of the routine or in its variables.
TIP: If you don't declare a subroutine in the .h file you should get a warning when you compile. If the object is declared the delegate and the subroutine is a delegate you shouldn't get the warning - this is a subtle hint that you have successfully overridden the existing delegate object.

Note: You should bracket your code postings with { code } tags (without the spaces) to get your code blocked out and untampered with on display.

HTH,

=Tod

Dec 21, 2007 10:16 PM in response to Nawyecky

Ok in documentation for the method textDidEndEditing: you can find in the discussion section:

«After validating the new value, posts an NSControlTextDidEndEditingNotification to the default notification center. This posting causes the receiver’s delegate to receive a controlTextDidEndEditing: message.»

I've try implementing controlTextDidEndEditing: and it worked.

But you shoud consider
{- (void)controlTextDidChange:(NSNotification *)aNotification}

I think it would be more appropriate.

Dec 22, 2007 12:59 AM in response to Vince Burn

ok, the error got fixed after vince's example: the correct method is, of course, controlTextDidChange (not simply textDidChange ). i had delegated the object properly as i wrote in my previous post, it was simply a method name error.

now that i understand this, two things remain unclear:

1. in the "nstextfield class reference" documentation, the method name is clearly textDidChange:. how am i supposed to know that it needs to be controlTextDidChange (short of posting a topic here everytime i need to delegate an object)?

2. i have several textfields in my window. how do i know which one i'm checking when i get the notification? suppose i delegate them all to my appcontroller object, how can appcontroller know which textfield has just been edited?

thanks a lot.

Dec 22, 2007 2:59 AM in response to Nawyecky

All the methods that were noted here are notification methods, they're meant to tell you that a certain action has been performed but you can't actually prevent a bad action (entering numbers for example) to be performed with those methods.

I think there are 2 better ways to work this out.

First you can use the NSControl delegate method to validate a value -\[NSControl control:isValidObject:\]

But you can also use an NSFormatter, and there you'll have a complete control over it. You can subclass it, and overload the method -\[NSFormatter isPartialStringValid:newEditingString:errorDescription:\] to modify the text inside the control every time the person types a letter and avoid the person to type anything invalid.

Dec 22, 2007 5:27 AM in response to Nawyecky

Nawyecky wrote:
1. in the "nstextfield class reference" documentation, the method name is clearly textDidChange:. how am i supposed to know that it needs to be controlTextDidChange (short of posting a topic here everytime i need to delegate an object)?


Well, in Objective-C (and every OO languages), you can send messages that are declared in the class of your object, but also every method that are declared in their superclasses. So that controlTetDidChange: is simply defined in the NSControl class which is the NSTextField super-class. That's called inheritance.

Nawyecky wrote:
2. i have several textfields in my window. how do i know which one i'm checking when i get the notification? suppose i delegate them all to my appcontroller object, how can appcontroller know which textfield has just been edited?


For that, you need to add outlets to your AppController class. An outlet is simply an instance variable which type is an object but that will be initialized by the nib file instead of being initialized explicitly by you.

You can define an outlet directly in Interface Builder for your class (don't forget to sync Xcode and IB by writing class files, if you already have written class files, the best way is to define the outlets in Xcode and read the files from IB), and then link your outlet to the object you want in IB.
Then, in your method implementation, you can see which object is sending the notification or the delegate method by comparing addresses :

AppController.h :

#import <Cocoa/Cocoa.h>
@interface AppController : NSObject
{
// Your outlets, IBOutlet is just part of the Interface Builder parser
IBOutlet NSTextField *numberField;
IBOutlet NSTextField *nameField;
// the rest of your ivars
}
// your methods
@end


AppController.m

#import "AppController.h"
@implementation
- (void)controlTextDidChange:(NSNotification *)aNotification
{
if([aNotification object] == numberField)
{
// your code if the number field send the notification
}
else if([aNotification object] == nameField)
{
// your code for the name field
}
}
@end


When you have a notification, you can generally retrieve the object who post the notification using the -[NSNotification object] message. As objects, in Objective-C, are always references to objects, which means that you manipulate addresses and not datas directly, you can simply compare with == operator the adresses to see which specific object you manipulate.

NB : You can't use a switch() {} structure to compare addresses, that's sad but that's like that sorry.

Also, when you use pure delegate methods... I don't consider Notifications to be pure delegate methods, those messages are sent to the delegate but also a notification is posted at the same time, whenever a notification is just a message sent to the delegate method.
So when you use pure delegate method you can retrieve the object that sent the message with the first argument of the method, that's how you distinguish notification from delegate methods.
For example :
- (BOOL)control:(NSControl *)control isValidObject:(id)object

Here, "control" argument is the object that sent the message, so you'll simply compare control with your outlets using == operator.

PS : Notifications are made to do actions depending on what happened in an object but not to modify the object that posted the notification, they're here just to tell that's something did happen. Wherever pure delegate methods are here to influence the object that sent the message, for example to prevent the object from doing something stupid (closing the app without saving for example), or even to confirm a good action.

Message was edited by: PsychoH13

This thread has been closed by the system or the community team. You may vote for any posts you find helpful, or search the Community for additional answers.

restricting character input in nstextfield

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple Account.