22

I would like to be able to detect if some text is changed in a UITextField so that I can then enable a UIButton to save the changes.

Alex Cio
  • 6,014
  • 5
  • 44
  • 74
RGriffiths
  • 5,722
  • 18
  • 72
  • 120

7 Answers7

40

Instead of observing notifications or implementing textField:shouldChangeCharacterInRange:replacementString:, it's easier to just add an event target:

[textField addTarget:self
              action:@selector(myTextFieldDidChange:)
    forControlEvents:UIControlEventEditingChanged];

- (void)myTextFieldDidChange:(id)sender {
    // Handle change.
}

Note that the event is UIControlEventEditingChanged and not UIControlEventValueChanged!

The advantages over the other two suggested solutions are:

  • You don't need to remember to unregister your controller with the NSNotificationCenter.
  • The event handler is called after the change has been made which means textField.text contains the text the user actually entered. The textField:shouldChangeCharacterInRange:replacementString: delegate method is called before the changes have been applied, so textField.text does not yet give you the text the user just entered – you'd have to apply the change yourself first.
DarkDust
  • 90,870
  • 19
  • 190
  • 224
  • how can I detect the Backspace key press event in this method 'myTextFieldDidChange'. Means could you explain the 'you'd have to apply the change yourself first.' part from your answer? – ViruMax Mar 28 '16 at 13:39
  • 1
    @ViruMax: The notification is not meant to detect single key presses. However, if you save the text field's value in a property that you update in the notification handler, you can compare the lengths before updating the property and you thus know whether characters have been removed, replaced or added. Regarding "you'd have to apply the change yourself": see [the answer that deals with the `UITextFieldDelegate` method](http://stackoverflow.com/a/15490405/400056) (you need to call `stringByReplacingCharactersInRange:withString:` to get result string of the change). – DarkDust Mar 28 '16 at 18:07
  • 1
    This method doesn't work for autocorrection, when user entered text with misprint and then tap again on the same textField. This method wouldn't been invoked. :[ – Konstantin Jun 28 '18 at 12:18
36

Take advantage of the UITextFieldTextDidChange notification or set a delegate on the text field and watch for textField:shouldChangeCharactersInRange:replacementString.

If you want to watch for changes with a notification, you'll need something like this in your code to register for the notification:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFieldDidChange:) name:UITextFieldTextDidChangeNotification object:theTextField];

Here theTextField is the instance of UITextField that you want to watch. The class of which self is an instance in the code above must then implement textFieldDidChange, like so:

- (void)textFieldDidChange:(NSNotification *)notification {
    // Do whatever you like to respond to text changes here.
}

If the text field is going to outlive the observer, then you must deregister for notifications in the observer's dealloc method. Actually it's a good idea to do this even if the text field does not outlive the observer.

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    // Other dealloc work
}
Shiva_iOS
  • 47
  • 1
  • 6
Aaron Golden
  • 7,092
  • 1
  • 25
  • 31
  • Thanks for the help but I am struggling with the syntax. I think it should be something like this - (void)UITextFieldTextDidChange:(UITextField *)textField { action to take } but it doesn't seem to work – RGriffiths Mar 19 '13 at 01:43
  • For notifications you must register an observer for the notification with NSNotificationCenter. Take a look at Apple's documentation for NSNotificationCenter. This is a very common pattern in iOS and Mac OS X application development. – Aaron Golden Mar 19 '13 at 01:46
  • Thanks again. I think I get it but where do I put the initial NSNotifcationCenter line? It throws up an error wherever I put it - Use of undeclared identifier of 'self'. Also how does it know to which TextField I am checking for changes? Is this object:theTextView? – RGriffiths Mar 19 '13 at 02:38
  • Frequently it will be in your observer's init method (or awakeFromNib if the observer is loaded from a xib file, or viewDidLoad if the observer is an instance of UIViewController). It's hard to be specific without knowing more about your program's structure. The error you're seeing indicates that you've put the initial line outside of any instance method. I encourage you to study the sample code linked to Apple's documentation here: https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/Reference/Reference.html – Aaron Golden Mar 19 '13 at 02:59
  • I have looked at some of the examples and now have it sitting in ViewDidLoad without any errors. I have changed the object to the name of my text field and added the other (void) methods. All looks good but when I run it and change any text in this field nothing happens. Don't worry - you have been a tremendous help and I'll keep at it. Thanks. – RGriffiths Mar 19 '13 at 03:05
  • NSNotifications are the wrong way to do this. Use UITextFieldDelegate methods. Less code and it's more maintainable. – barndog Dec 24 '13 at 19:23
9

For that, first you need to have your textfield have it delegate reference assigned. And the delgate, should preferably be, the vew controller which is the files owner of the view. Which goes like

myTextField.delegate = myViewControllerReferenceVariable

And in your viewController interface, tell you will be implementing UITextFieldDelegate by

@interface MyViewController:UIViewController<UITextFieldDelegate>

And in your view controller implementation override

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string

So the code will look like

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
     {
        text = [textfield.text stringByReplacingCharactersInRange:range withString:string];
        if (textfield == refToTextFieldYouWantToCheck) {
            if ( ! [textToCheck isEqualToString:text] ) {
               [theButtonRef setEnabled:YES];
            } 
         }
           return YES; //If you don't your textfield won't get any text in it
      }

You can also subscribe to notification which is sort of messy IMHO You can find how to do it here.

Community
  • 1
  • 1
egghese
  • 2,193
  • 16
  • 26
  • Again, I really appreciate the help but is this not just seeing if some text is entered? (ie length is now > 0) There is text already in the textfield and I want detect if it is changed. – RGriffiths Mar 19 '13 at 02:27
  • @RichardGriffiths Sorry, i thought you wanted to check the existence of a content, I have corrected it now. Are you trying to put placeholder text ? – egghese Mar 19 '13 at 05:21
  • 2
    This is the correct answer, not sure why it's not the accepted one. This particular method will be trigger whenever a new character is entered or deleted. If you just want to check if the content length is greater than 0, then use one of the other delegate methods like didBeginEditing, ect – barndog Dec 24 '13 at 19:23
  • This is _not_ the correct way to solve this as this method is called before the change is done to the text. In other words, if you already typed in `fo` and then type another `o`, the `textField.text` is still `fo` and not yet `foo`! You'd have to apply the change yourself. – DarkDust Jul 17 '14 at 09:41
  • 1
    @DarkDust I wouldn't say either method is wrong, with registering for notification you end up with a lot more stuff to manage like registering and then removing the observers. When you can do it right from one delegate, a lot of less code. Now about your concern, the method will have latest string with ``stringByReplacingCharactersInRange``. Check the edited code. – egghese Mar 02 '16 at 15:57
3

Swift 3.0

Process 1

Create IBOutlet of UITextfiled and Add Target to text field.

 m_lblTxt.addTarget(self, action: #selector(self.textFieldDidChange), for: UIControlEvents.editingChanged)

 func textFieldDidChange(textField:UITextField)
 {
     NSLog(textField.text!)
 }

Process 2

 m_lblTxt.delegate = self

 //MARK: - TextField Delegates
 func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool 
 {
      print(textField.text!)
      return true
 }
Sunny
  • 63
  • 7
2

This can be accomplished in Interface Builder on the Editing Changed event of UITextField. Drag from it to your code and create an IBAction.

For example:

@IBAction func textFieldChanged(_ sender: UITextField) {
  print(sender.text)
}

This event is the same as described in other answers here in that the .text property contains the updated text input when it gets triggered. This can help clean up code clutter by not having to programmatically add the event to every UITextField in the view.

Mark Suman
  • 10,430
  • 2
  • 27
  • 23
1

You could create a variable to store the original string, then register with the notification center to receive UITextFieldTextDidChangeNotification event:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateButton:) name:UITextFieldTextDidChangeNotification object:nil];

Then, create a method to receive the notification, and compare the current value of the text field with the original value

-(void) updateButton:(NSNotification *)notification {
       self.myButton.enabled = ![self.myTextField.text isEqualToString:originalString];
}

Don't forget to de-register the notification when the view controller is deallocated.

[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidChangeNotification object:nil];
Firas
  • 41
  • 1
  • 6
-3

You can add a class member like this NSString *changeTemp,then

changetemp = textfield;

if( change temp != textfild ){
    changetemp=textfild;
    NSLog(@" text is changed"
} else { 
    NSLog(@" text isn't changed"):
}
Alex Cio
  • 6,014
  • 5
  • 44
  • 74
Tung
  • 21
  • 5