120

I'm using the code below to try and have textField2's text content get updated to match textField1's whenever the user types in textField1.

- (BOOL) textField: (UITextField *)theTextField shouldChangeCharactersInRange: (NSRange)range replacementString: (NSString *)string {    
  if (theTextField == textField1){    
     [textField2 setText:[textField1 text]];    
  }
}

However, the output I observe is that...

textField2 is "12", when textField1 is "123"

textField2 is "123", when textField1 is "1234"

... when what I want is:

textField2 is "123", when textField1 is "123"

textField2 is "1234", when textField1 is "1234"

What am I doing wrong?

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
user265961
  • 1,353
  • 3
  • 10
  • 10
  • 9
    Just a reminder it is staggeringly easier to always use the **"Editing Changed" event** .. just drag it in IB to a function you make. – Fattie Dec 10 '13 at 17:27
  • Note the editing changed event does not capture any text changing events generated programmatically, e.g. autocorrect/autocompletion/text replacement. – shim Mar 16 '20 at 18:00

10 Answers10

273

-shouldChangeCharactersInRange gets called before text field actually changes its text, that's why you're getting old text value. To get the text after update use:

[textField2 setText:[textField1.text stringByReplacingCharactersInRange:range withString:string]];
Vladimir
  • 170,431
  • 36
  • 387
  • 313
  • 15
    This *almost* worked for me. If I typed a character, this worked. If I pressed the delete button, it'd delete two characters. For me, the following suggestion worked: http://stackoverflow.com/questions/388237/getting-the-value-of-a-uitextfield-as-keystrokes-are-entered Basically, drag'n'drop from the UITextField into your code (to create a function), then right-click on your TextField, and drag'n'drop from the circle for the "Editing Changed" to your new function. (Sigh. I miss Visual Studio sometimes..) – Mike Gledhill May 12 '13 at 18:33
  • I also feel this is a valid answer, but not THE answer. The best way to accomplish what you want was answered by @tomute – Pedro Borges Sep 09 '14 at 09:08
  • 5
    Or textFiel.text = (textFiel.text as NSString).stringByReplacingCharactersInRange(range, withString: string) in Swift – Max Nov 17 '14 at 10:16
  • @MikeGledhill You can do the same thing programmatically: `[textField addTarget:self action:@selector(textFieldEditingChanged:) forControlEvents:UIControlEventEditingChanged]` – Steve Moser Jun 19 '17 at 18:11
53
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    NSString * searchStr = [textField.text stringByReplacingCharactersInRange:range withString:string];

    NSLog(@"%@",searchStr);
    return YES;
}
btmanikandan
  • 1,923
  • 2
  • 25
  • 45
42

Swift 3

Based on the accepted answer, the following should work in Swift 3:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    let newString = NSString(string: textField.text!).replacingCharacters(in: range, with: string)

    return true
}

Note

Both String and NSString have methods called replacingCharacters:inRange:withString. However, as expected, the former expects an instance of Range, while the latter expects an instance of NSRange. The textField delegate method uses an NSRange instance, thus the use of NSString in this case.

focorner
  • 1,927
  • 1
  • 20
  • 24
26

Instead of using the UITextFieldDelegate, try to use "Editing Changed" event of UITextField.

Community
  • 1
  • 1
tomute
  • 2,673
  • 2
  • 20
  • 17
14

In Swift(4), without NSString (pure Swift):

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    if let textFieldString = textField.text, let swtRange = Range(range, in: textFieldString) {

        let fullString = textFieldString.replacingCharacters(in: swtRange, with: string)

        print("FullString: \(fullString)")
    }

    return true
}

As an extension:

extension UITextField {

    func fullTextWith(range: NSRange, replacementString: String) -> String? {

        if let fullSearchString = self.text, let swtRange = Range(range, in: fullSearchString) {

            return fullSearchString.replacingCharacters(in: swtRange, with: replacementString)
        }

        return nil
    }
}

// Usage:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    if let textFieldString = textField.fullTextWith(range: range, replacementString: string) {
        print("FullString: \(textFieldString)")
    }

    return true
}
bauerMusic
  • 5,470
  • 5
  • 38
  • 53
8

Swift version for it :

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {

    if string == " " {
        return false
    }

    let userEnteredString = textField.text

    var newString = (userEnteredString! as NSString).stringByReplacingCharactersInRange(range, withString: string) as NSString

    print(newString)

    return true
}
ioopl
  • 1,735
  • 19
  • 19
5

This is the code you need,

if ([textField isEqual:self.textField1])
  textField2.text = [textField1.text stringByReplacingCharactersInRange:range withString:string];
Deepak Danduprolu
  • 44,595
  • 12
  • 101
  • 105
1

use guard

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
        guard case let textFieldString as NSString = textField.text where
            textFieldString.stringByReplacingCharactersInRange(range, withString: string).length <= maxLength else {
                return false
        }
        return true
    }
ober
  • 2,363
  • 1
  • 19
  • 17
0

My solution is to use UITextFieldTextDidChangeNotification.

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

Don't forget to call [[NSNotificationCenter defaultCenter] removeObserver:self]; in dealloc method.

boweidmann
  • 3,312
  • 2
  • 20
  • 27
0

If you need to replace the textfield text with this you can use my solution (Swift 3): https://gist.github.com/Blackjacx/2198d86442ec9b9b05c0801f4e392047

After the replacement you can just get textField.text to retrieve the composed text.

blackjacx
  • 9,011
  • 7
  • 45
  • 56