326

I am trying to check when a text field changes, equivalent too the function used for textView - textViewDidChange so far I have done this:

  func textFieldDidBeginEditing(textField: UITextField) {
        if self.status.text == "" && self.username.text == "" {
            self.topRightButton.enabled = false
        } else {   
            self.topRightButton.enabled = true
        }
    }

Which kind of works, but the topRightButton is enabled as soon as the text field is pressed on, I want it to be enabled only when text is actually typed in?

boraseoksoon
  • 1,792
  • 1
  • 16
  • 23

19 Answers19

818

SWIFT

Swift 4.2

textfield.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), for: .editingChanged)

and

@objc func textFieldDidChange(_ textField: UITextField) {

}

SWIFT 3 & swift 4.1

textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), for: .editingChanged)

and

func textFieldDidChange(_ textField: UITextField) {

}

SWIFT 2.2

textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), forControlEvents: UIControlEvents.EditingChanged)

and

func textFieldDidChange(textField: UITextField) {
    //your code
}

OBJECTIVE-C

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

and textFieldDidChange method is

-(void)textFieldDidChange :(UITextField *) textField{
    //your code
}
Pranav Kasetti
  • 4,773
  • 2
  • 16
  • 38
Fawad Masud
  • 11,427
  • 3
  • 23
  • 32
  • This crashes for me and I don't understand why. – Levi Roberts Jun 13 '15 at 03:40
  • 1
    Checked multiple times. Delegate is set immediately before it inside `viewDidLoad`. The action is letter for letter the same. The app crashes as soon as a keyboard button is pressed. Edit: Figured it out! Was missing the semicolon inside the action. I assumed it only had to be same as the function name. – Levi Roberts Jun 13 '15 at 08:17
  • @FawadMasud this does nothing now in Swift 2.0 on iOS 9 with XCode 7 has it been depreciated or do you know the current way to fix it? – Cody Weaver Oct 13 '15 at 07:28
  • @CodyWeaver I just checked it on XCode 7, iOS 9 and it is working fine. Make sure there are no spelling mistakes in the function you are naming. – Fawad Masud Oct 13 '15 at 07:53
  • @FawadMasud yeah it worked fine. How can I call this method or `textFieldDidEndEditing` if I change the `UITextField`'s text from another object? – Cody Weaver Oct 13 '15 at 08:01
  • @CodyWeaver you can use delegates or you can use notifications. – Fawad Masud Oct 13 '15 at 08:04
  • The `UITextField`'s delegate is itself. – Cody Weaver Oct 13 '15 at 08:06
  • @CodyWeaver No I am not talking about UITextField's delegate. You have to create your own delegate methods in the current class and call these methods from the other class.http://stackoverflow.com/questions/24099230/delegates-in-swift – Fawad Masud Oct 13 '15 at 08:14
  • Target/Action and Delegates are two totally different ways to get the action. – Bryan Bryce Feb 04 '16 at 22:38
  • I had to change the code for Swift to `textField.addTarget(self, action: #selector(MyViewController.textFieldDidChange(_:)), forControlEvents: UIControlEvents.EditingChanged)` to make it work. – Wim Deblauwe May 11 '16 at 12:39
  • @WimDeblauwe that is for swift 2.2. I will update it for swift 2.2 as well. Thanks for pointing. – Fawad Masud May 11 '16 at 12:51
  • @FawadMasud Usually you don't have a single textField in a view. How can I refer to all textFields and then addTarget(self, action:) ? Should I loop through all textFileds that are in the View and inside the for loop do nameOfTextField.addTarget(self, action:...) Is there a less verbose way? – bibscy Mar 04 '17 at 13:32
  • 1
    @bibscy yes you have to loop through all textfields inside a view. – Fawad Masud Mar 04 '17 at 14:53
  • 1
    For Swift 4.2 its: Texttfield.addTarget(self, action: #selector(ViewControllerr.textFieldDidChange(_:)), for: UIControl.Event.editingChanged) – Exitare Jun 28 '18 at 19:35
  • For Swift 4.2 there's a warning not to use @objc which you need when you use #selector. So, what's the real solution for Swift 4.2/XCode 10? – LevinsonTechnologies Oct 28 '18 at 02:21
  • Update: @Exitare, swift does not require the ViewController. in the #selector. This changed happened in later swift 3.x. What is needed for mine to work was to add the UITextFieldDelegate to the class and add textField.delegate = self. – David Sanford Jan 19 '19 at 23:38
  • I know and changed my usage but I totally forgot to change my answer here. – Exitare Jan 19 '19 at 23:41
  • Does not called on TextField Clear Button press. – Argus Jun 24 '19 at 16:00
  • @Argus you need to subclass `UITextField` and override `deleteBackward` method – Leo Dabus Sep 04 '20 at 21:07
140

You can make this connection in interface builder.

  1. In your storyboard, click the assistant editor at the top of the screen (two circles in the middle). Assistant editor selected

  2. Ctrl + Click on the textfield in interface builder.

  3. Drag from EditingChanged to inside your view controller class in the assistant view. Making connection

  4. Name your function ("textDidChange" for example) and click connect. Naming function

rmooney
  • 5,597
  • 3
  • 25
  • 28
73

Swift 5.0

textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)),
                          for: .editingChanged)

and handle method:

@objc func textFieldDidChange(_ textField: UITextField) {

}

Swift 4.0

textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)),
                          for: UIControlEvents.editingChanged)

and handle method:

@objc func textFieldDidChange(_ textField: UITextField) {

}

Swift 3.0

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

and handle method:

func textFieldDidChange(textField: UITextField) { 

}
Robert
  • 3,312
  • 20
  • 41
34

The way I've handled it so far: in UITextFieldDelegate

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
    // text hasn't changed yet, you have to compute the text AFTER the edit yourself
    let updatedString = (textField.text as NSString?)?.stringByReplacingCharactersInRange(range, withString: string)

    // do whatever you need with this updated string (your code)


    // always return true so that changes propagate
    return true
}

Swift4 version

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let updatedString = (textField.text as NSString?)?.replacingCharacters(in: range, with: string)
    return true
}
iman kazemayni
  • 955
  • 1
  • 16
  • 18
Vinzzz
  • 11,538
  • 4
  • 34
  • 42
14

Swift 3

 textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(sender:)), for: UIControlEvents.editingChanged)
Alessandro Mattiuzzi
  • 1,871
  • 1
  • 15
  • 24
9

textField(_:shouldChangeCharactersIn:replacementString:) worked for me in Xcode 8, Swift 3 if you want to check every single keypress.

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

    // Whatever code you want to run here.
    // Keep in mind that the textfield hasn't yet been updated,
    // so use 'string' instead of 'textField.text' if you want to
    // access the string the textfield will have after a user presses a key

    var statusText = self.status.text
    var usernameText = self.username.text

    switch textField{
    case self.status:
        statusText = string
    case self.username:
        usernameText = string
    default:
        break
    }

    if statusText == "" && usernameText == "" {
        self.topRightButton.enabled = false
    } else {   
        self.topRightButton.enabled = true
    }

    //Return false if you don't want the textfield to be updated
    return true
}
radthemad4
  • 145
  • 1
  • 8
7

Swift 3.0.1+ (Some of the other swift 3.0 answers are not up to date)

textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)),
                          for: UIControlEvents.editingChanged)

func textFieldDidChange(_ textField: UITextField) {

}
aviran
  • 4,456
  • 6
  • 40
  • 66
5

You can use this delegate method from UITextFieldDelegate. It fires with every character change.

(Objective C) textField:shouldChangeCharactersInRange:replacementString:
(Swift) textField(_:shouldChangeCharactersInRange:replacementString:)

However THIS ONLY FIRES BEFORE a change is made (indeed, a change is only made if you do return true from here).

Fattie
  • 30,632
  • 54
  • 336
  • 607
Abubakr Dar
  • 4,008
  • 4
  • 20
  • 28
  • 1
    How should this be wrote as I have also tried this method and come to the same solution where it only changes once the textField is activated, not once the text actually changes?? –  Feb 08 '15 at 14:33
  • When you implement the above delegate method, it fires every time you change your text. You only need to add this code, self.textfield.delegate = self – Abubakr Dar Feb 08 '15 at 17:31
  • For me, this method didn't work because you couldn't check if the textfield was empty inside the method. Primarily because it returns true/false depending on IF the textfield can change. So the event fires BEFORE the textfield has had a chance to become empty. – Levi Roberts Jun 13 '15 at 08:27
  • @LeviRoberts, You have a reference to textfield inside this method. So you can check if the textfield.text is empty. – Abubakr Dar Jun 13 '15 at 12:49
  • You don't seem to understand. When it is empty, the `.isEmpty` method does not equate to true until AFTER this method has had a chance to return true; to tell the app that the textfield should change. – Levi Roberts Jun 13 '15 at 14:01
  • As per the protocol reference; "Asks the delegate if the specified text should be changed." https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextFieldDelegate_Protocol/#//apple_ref/occ/intfm/UITextFieldDelegate/textField:shouldChangeCharactersInRange:replacementString: – Levi Roberts Jun 13 '15 at 14:02
5

Swift 4

Conform to UITextFieldDelegate.

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    // figure out what the new string will be after the pending edit
    let updatedString = (textField.text as NSString?)?.replacingCharacters(in: range, with: string)

    // Do whatever you want here


    // Return true so that the change happens
    return true
}
drewster
  • 3,645
  • 2
  • 27
  • 36
5

There's now a UITextField delegate method available on iOS13+

optional func textFieldDidChangeSelection(_ textField: UITextField)
JP Aquino
  • 2,958
  • 1
  • 17
  • 22
3

Maybe use RxSwift ?

need

pod 'RxSwift',    '~> 3.0'
pod 'RxCocoa',    '~> 3.0'

add imports obviously

import RxSwift
import RxCocoa

So u have a textfield : UITextField

let observable: Observable<String?> = textField.rx.text.asObservable()
observable.subscribe(
            onNext: {(string: String?) in
                print(string!)
        })

U have other 3 methods..

  1. onError
  2. onCompleted
  3. onDisposed
  4. onNext
marlonpya
  • 575
  • 6
  • 19
  • To receive events of real change only and not also when textfield became first responder, you have to use distinctUntilChanged on text. – RealNmae Nov 10 '19 at 08:56
1

Swift 4

textField.addTarget(self, action: #selector(textIsChanging), for: UIControlEvents.editingChanged)

@objc func textIsChanging(_ textField:UITextField) {

 print ("TextField is changing")

}

If you want to make a change once the user has typed in completely (It will be called once user dismiss keyboard or press enter).

textField.addTarget(self, action: #selector(textDidChange), for: UIControlEvents.editingDidEnd)

 @objc func textDidChange(_ textField:UITextField) {

       print ("TextField did changed") 
 }
Sid Mhatre
  • 2,880
  • 1
  • 14
  • 35
Anamika
  • 21
  • 5
1
txf_Subject.addTarget(self, action:#selector(didChangeFirstText), for: .editingChanged)

@objc func didChangeText(textField:UITextField) {
    let str = textField.text
    if(str?.contains(" "))!{
        let newstr = str?.replacingOccurrences(of: " ", with: "")
        textField.text = newstr
    }
}

@objc func didChangeFirstText(textField:UITextField) {
    if(textField.text == " "){
        textField.text = ""
    }
}
Yogesh Tandel
  • 1,539
  • 1
  • 16
  • 18
1

You should follow this steps:

  1. Make a Outlet reference to the textfield
  2. AssignUITextFieldDelegate to the controller class
  3. Configure yourTextField.delegate
  4. Implement whatever function you need

Sample code:

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var yourTextFiled : UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()

        yourTextFiled.delegate = self
    }


    func textFieldDidEndEditing(_ textField: UITextField) {
        // your code
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        // your code
    }

    .
    .
    .
}
M.Hazara
  • 102
  • 8
1

Swift 4.2

write this in viewDidLoad

// to detect if TextField changed
TextField.addTarget(self, action: #selector(textFieldDidChange(_:)),
                                   for: UIControl.Event.editingChanged)

write this outside viewDidLoad

@objc func textFieldDidChange(_ textField: UITextField) {
    // do something
}

You could change the event by UIControl.Event.editingDidBegin or what ever you want to detect.

Gerges Eid
  • 851
  • 10
  • 14
0

This is how you can add a textField text change listener using Swift 3:

Declare your class as UITextFieldDelegate

override func viewDidLoad() {
    super.viewDidLoad()

    textField.delegate = self

    textField.addTarget(self, action: #selector(UITextFieldDelegate.textFieldShouldEndEditing(_:)), for: UIControlEvents.editingChanged)
}

Then just traditionally add a textFieldShouldEndEditing function:

func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { // do stuff
        return true 
}
Codetard
  • 2,026
  • 22
  • 33
0

Just in case you are interested in a SwiftUI solution, this it's working for me:

 TextField("write your answer here...",
            text: Binding(
                     get: {
                        return self.query
                       },
                     set: { (newValue) in
                        self.fetch(query: newValue) // any action you need
                                return self.query = newValue
                      }
            )
  )

I have to say it's not my idea, I read it in this blog: SwiftUI binding: A very simple trick

abanet
  • 1,127
  • 14
  • 19
0

In case it is not possible to bind the addTarget to your UITextField, I advise you to bind one of them as suggested above, and insert the code for execution at the end of the shouldChangeCharactersIn method.

nameTextField.addTarget(self, action: #selector(RegistrationViewController.textFieldDidChange(_:)), for: .editingChanged)

@objc func textFieldDidChange(_ textField: UITextField) {
    if phoneNumberTextField.text!.count == 17 && nameTextField.text!.count > 0 {
        continueButtonOutlet.backgroundColor = UIColor(.green)
    } else {
        continueButtonOutlet.backgroundColor = .systemGray
    }
}

And in call in shouldChangeCharactersIn func.

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

    guard let text = textField.text else {
        return true
    }
    let lastText = (text as NSString).replacingCharacters(in: range, with: string) as String

    if phoneNumberTextField == textField {
        textField.text = lastText.format("+7(NNN)-NNN-NN-NN", oldString: text)
        textFieldDidChange(phoneNumberTextField)
        return false
    }
    return true
}
-2

swift 4

In viewDidLoad():

    //ADD BUTTON TO DISMISS KEYBOARD

    // Init a keyboard toolbar 
    let toolbar = UIView(frame: CGRect(x: 0, y: view.frame.size.height+44, width: view.frame.size.width, height: 44))
    toolbar.backgroundColor = UIColor.clear

    // Add done button
    let doneButt = UIButton(frame: CGRect(x: toolbar.frame.size.width - 60, y: 0, width: 44, height: 44))
    doneButt.setTitle("Done", for: .normal)
    doneButt.setTitleColor(MAIN_COLOR, for: .normal)
    doneButt.titleLabel?.font = UIFont(name: "Titillium-Semibold", size: 13)
    doneButt.addTarget(self, action: #selector(dismissKeyboard), for: .touchUpInside)
    toolbar.addSubview(doneButt)

    USDTextField.inputAccessoryView = toolbar

Add this function:

    @objc func dismissKeyboard() {
      //Causes the view (or one of its embedded text fields) to resign the first responder status.
      view.endEditing(true)
    }
Nil
  • 119
  • 3
  • 11