Im trying to implement a delegate for a NSTextField object so I can detect the user input in real time and give some feedback about no allowed input in that particular field.
Especially, I want to simulate the onChange() method from JavaScript, detecting the user input in real time and show him a warning if it is writing a non supported value.
i.e. The app have a text field it only accept numeric values from 0 to 255 (like RGB values) and I want to know when the user is writing not numeric values or out of range values to instantly show him a warning message or change the text field background color, just a visual hint to let him know the input it's wrong.
Like you see on the pictures above, I want to show a warning sign every time the user inputs a forbidden value in the text field.
I have been reading a lot of the Apple's documentation but I don't understand which delegate to implement (NSTextFieldDelegate, NSTextDelegate, or NSTextViewDelegate), also, I have no idea how to implement it in my AppDelegate.m file and which method use and how to get the notification of user editing.
Right now, I already set the Delegate in my init method with something like this [self.textField setDelegate:self]; but I don't understand how to use it or which method implements.
I found a solution using the information posted in this question... Listen to a value change of my text field
First of all I have to declare the NSTextFieldDelegate in the AppDelegate.h file
#interface AppDelegate : NSObject <NSApplicationDelegate, NSTextFieldDelegate>
After that, I have to instantiate the delegate for the NSTextField object I want to modify while the user update it in the AppDelegate.m file.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[self.textField setDelegate:self];
}
Finally, I implement the methods to detect field editing with the changes I want to set.
- (void)controlTextDidChange:(NSNotification *)notification {
NSTextField *textField = [notification object];
if ([textField doubleValue] < 0 | [textField doubleValue] > 255) {
textField.textColor = [NSColor redColor];
}
}
- (void)controlTextDidEndEditing:(NSNotification *)notification {
NSTextField *textField = [notification object];
if ([textField resignFirstResponder]) {
textField.textColor = [NSColor blackColor];
}
}
Make your class conform to the NSTextFieldDelegate protocol. It need's to be that protocol because in the documentation it says the type of protocol the delegate conforms to.
#interface MyClass : NSObject
And implement the delegate's methods (just add them to your code). Example
- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor
{
}
EDIT:
I think in your case it would be better to replace the TextField for a TextView and use a NSTextViewDelegate, in the delegate, the method of most interst of you should be
- (BOOL)textView:(NSTextView *)aTextView shouldChangeTextInRange:(NSRange)affectedCharRange replacementString:(NSString *)replacementString
{
BOOL isValid = ... // Check here if replacementString is valid (only digits, ...)
return isValid; // If you return false, the user edition is cancelled
}
Related
I am still learning Objective C/Cocoa and I am building a program with a simples interface. In this interface, there are some NSTextField which has this delegate:
- (void) controlTextDidChange: (NSNotification *) obj{
//Some code here
}
When the user changes the the text of any of these NSTextField the program needs to check if the number inside the NSTextField is an integer. If the provided string is not an Integer I want to display a dialog with the error and each NSTexField the error occurred, since I have more than one NSTextField connected to this method.
My question is: how can I discover each UI object sent the message to the controlTextDidChange method?
Thanks in advance.
- (void)controlTextDidChange:(NSNotification *)anotif
{
if ([anotif object]==field1)
{
// field1 processing
}
else
{
// field2 processing
}
}
From
controlTextDidChange with 2 nstextfields - call different selectors
If you don't have the UITextField subviews as properties, you can set tag for every UITextField subview change the code #Bruno provided as follows:
- (void)controlTextDidChange:(NSNotification *)anotif
{
UITextField *textField = (UITextField *)[anotif object];
if (textField.tag == 1)
{
// field1 processing
}
else
{
// field2 processing
}
}
I just implemented following method that suppose to take some action after the value of a NSTextField is changed in my NSOutlineView
-(BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor
{
NSLog(#"end editing");
NSTextField* tf = (NSTextField*)control;
if(selectedItem && [selectedItem isKindOfClass:[HSCategoryClass class]])
{
HSCategoryClass* c = selectedItem;
c.name = tf.stringValue;
// request the update from DB
[[NSNotificationCenter defaultCenter] postNotificationName:#"updatingCategoryName"
object:c
userInfo:#{#"sender":self}];
}
return YES;
}
However, when I'm done editing and hit enter key or navigate anywhere outside of the text field this method is getting called twice instead of just once.
Does anyone know why is this?!
Any kind of help is highly appreciated!
That routine does not signify that editing has ended. Instead, it's called to find out if it should end (hence the name of the method). It can be called by the framework any number of times, and you shouldn't be relying on it for this purpose.
Instead override the NSOutlineView's textDidEndEditing: method.
Be sure to call super.
So you'd subclass the NSOutlineView and in your subclass:
- (void)textDidEndEditing:(NSNotification *)aNotification
{
// do your stuff
[super textDidEndEditing:aNotification];
}
Below is my typical WindowController module for presenting a modal dialog (could be settings, asking username/password, etc) loaded from a XIB. It seems a bit too complex for something like this. Any ideas how this can be done better/with less code?
Never mind that it's asking for a password, it could be anything. What frustrates me most is that I repeat the same pattern in each and every of my XIB-based modal window modules. Which of course means I could define a custom window controller class, but before doing that I need to make sure this is really the best way of doing things.
#import "MyPasswordWindowController.h"
static MyPasswordWindowController* windowController;
#interface MyPasswordWindowController ()
#property (weak) IBOutlet NSSecureTextField *passwordField;
#end
#implementation MyPasswordWindowController
{
NSInteger _dialogCode;
}
- (id)init
{
return [super initWithWindowNibName:#"MyPassword"];
}
- (void)awakeFromNib
{
[super awakeFromNib];
[self.window center];
}
- (void)windowWillClose:(NSNotification*)notification
{
[NSApp stopModalWithCode:_dialogCode];
_dialogCode = 0;
}
- (IBAction)okButtonAction:(NSButton *)sender
{
_dialogCode = 1;
[self.window close];
}
- (IBAction)cancelButtonAction:(NSButton *)sender
{
[self.window close];
}
+ (NSString*)run
{
if (!windowController)
windowController = [MyPasswordWindowController new];
[windowController loadWindow];
windowController.passwordField.stringValue = #"";
if ([NSApp runModalForWindow:windowController.window])
return windowController.passwordField.stringValue;
return nil;
}
The application calls [MyPasswordWindowController run], so from the point of view of the user of this module it looks simple, but not so much when you look inside.
Set tags on your buttons to distinguish them. Have them both target the same action method:
- (IBAction) buttonAction:(NSButton*)sender
{
[NSApp stopModalWithCode:[sender tag]];
[self.window close];
}
Get rid of your _dialogCode instance variable and -windowWillClose: method.
-[NSApplication runModalForWindow:] will already center the window, so you can get rid of your -awakeFromNib method.
Get rid of the invocation of -[NSWindowController loadWindow]. That's an override point. You're not supposed to call it. The documentation is clear on that point. It will be called automatically when you request the window controller's -window.
Get rid of the static instance of MyPasswordWindowController. Just allocate a new one each time. There's no point in keeping the old one around and it can be troublesome to reuse windows.
I have some textfields and I want to do when I change textfield1 text set text to other textfields. My code below. But it not works. How can I solve this?
- (IBAction)TCKimlikTextChange:(id)sender {
[TCKimlikText addTarget:self action:#selector(yourMethod: ) forControlEvents:UIControlEventEditingChanged];
}
-(void)yourMethod: (UITextField*)tf_{
if (tf_) {
if (TCKimlikText.text == #"1") {
AdinizText.text = #"Hacer";
}
}
}
Your code is very abstract. yourMethod, tf_ TCKimlikTextChange are all expressions that are not very human readable. You should work on your variable names.
I suppose your first method is a button handler. It just assigned a target and action to the text field, but does not call any method. You do not need that action if you use the delegate protocol.
To solve your problem: implement the UITextField delegate methods. Make sure you set the delegate (probably self) for your text fields. Your view controller must mention the <UITextFieldDelegate> protocol in its .h file. Thus, in textField:shouldChangeCharactersInRange:replacementString::
if ([textField.text isEqualToString:#"1"]) {
displayLabel.text = #"Hacer";
}
Notice that you need isEqualToString: to compare strings, a simple == won't do.
If u are want to change on the click of the return button use the delegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if(textField == field1)
[field2 setText:field1.text];
[field1 resignFirstResponder];
return YES;
}
or u can use other delegates too like:
– textFieldShouldBeginEditing:
– textFieldDidBeginEditing:
Here is my current code for my class...
#interface Stat : NSObject {
#private
IBOutlet NSTextField *value;
IBOutlet NSTextField *modValue;
IBOutlet NSStepper *stepper;
}
-(IBAction)setValue:(id)sender;
#end
#implementation Stat
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
-(IBAction)setValue:(id)sender
{
[value setIntValue:([sender intValue])];
[modValue setIntValue:(round(([sender intValue]/2)-5))];
}
#end
The stepper, value text box, and modValue text box are all linked to their corresponding variables, and the stepper is linked to the setValue function. However, when I use the stepper, only the text in the modValue text changes. Can anyone help? If you need any more code/info I can provide it.
Edit: Also. If you do have the solution, can you please explain a bit? I've coded in Java and C# for a long time now, however Obj-C is giving me a challenge. So far I love it though. :)
Edit: it is most likely that one of your outlets in Interface Builder is not properly set up. Check to make sure that both NSTextFields in IB are connected to the correct outlets in Xcode.
According to the Documentation,
"When the value changes, the stepper sends the UIControlEventValueChanged flag to its target (see addTarget:action:forControlEvents:). Refer to the description of the continuous property for information about whether value change events are sent continuously or when user interaction ends."
So you set the state for your UIStepper IBAction as UIControlEventValueChanged.