11

I have a subclass of UITextField that is specific to handle Date text. I have a tableviewcell that uses this text field:

let dateInput: DateTextField

Now the controller needs to initialize the text of the dateInput prior to display as follows:

cell.dateInput.text = "01/29/2016"

Now, I want to be able to detect that the text changed from the subclass so that I can update the internal date variable so that is it in-sync with the text.

I implemented the textfield delegate methods but that just catches changes made by the user and not programmatically.

Lukesivi
  • 2,206
  • 4
  • 25
  • 43
Joe.b
  • 422
  • 1
  • 6
  • 16
  • Maybe it's just me, but I don't understand what you're trying to do... Can you elaborate a little bit or make it clearer? – Lukesivi Jan 29 '16 at 22:01
  • I have a subclass of UItextField that displays only Date strings. A Controller is setting the text programmatically (dateTextField.text="01/12/2015"). So I am trying to figure out how to catch that the text has changed so that I can update some backend variables in the DateTextField class. Does this explain better? – Joe.b Jan 29 '16 at 22:20
  • since we are talking strings here, why not just do if condition where (dateInput.text == "01/29/2016")...? This can be done in an initial method such as viewWillAppear and based on that do asynchronous call to backend – ksa_coder Jan 29 '16 at 22:21

3 Answers3

10

You can override property and add didSet observer in your custom class:

class DateTextField: UITextField {

    override var text: String? {
        didSet {
           // Do your stuff here    
        }
    }   
}
Konstantine Kalbazov
  • 2,643
  • 26
  • 29
  • This is exactly what I was looking for. Thanks! – Joe.b Jan 30 '16 at 03:53
  • 23
    Internal iOS routines do not use the "text" nor "attributedText" properties, they are just for external use. For example, when the user is typing on your text field, the internal events are the only way to get the textField values, because UIKit is changing the internal variable only, not the "text" or "attributedText" properties. – user464230 Nov 29 '16 at 19:48
  • This works perfectly if you're setting your text somewhere (`textField.text = "some text"`) and you want the `UITextField` subclass to respond after that. – Jonathan Cabrera Jan 18 '19 at 19:33
5

My solution for this is to have each instance of the subclass maintain its own notification for UITextFieldDidChange and use a custom protocol to relay that information to the listener.

protocol MutableTextFieldDelegate {
    func textChanged(_ sender:MutableTextField)
}

class MutableTextField : UITextField {

    var textChangedDelegate : MutableTextFieldDelegate?

    var previousValue : String?

    override func awakeFromNib() {
        super.awakeFromNib()
        NotificationCenter.default.addObserver(forName: .UITextFieldTextDidChange, object: self, queue: nil) { [weak self] notification in
            guard let strongSelf = self else { return }
            guard let object = notification.object as? MutableTextField, object == strongSelf else { return }

            if strongSelf.previousValue != strongSelf.text {
                strongSelf.textChangedDelegate?.textChanged(strongSelf)
            }
            strongSelf.previousValue = strongSelf.text
        }
    }
}

swift5: NotificationCenter.default.addObserver(forName: UITextField.textDidChangeNotification ...

Anton Tropashko
  • 5,486
  • 5
  • 41
  • 66
James Webster
  • 31,873
  • 11
  • 70
  • 114
  • Don’t forget to remove the observer on `deinit()` here, otherwise these observers will linger forever – Pim Oct 14 '19 at 14:24
  • This is no longer true. "As of iOS 9 (and OS X 10.11), you don't need to remove observers yourself, if you're not using block based observers though. The system will do it for you, since it uses zeroing-weak references for observers, where it can." https://stackoverflow.com/a/40339926/916299 – James Webster Oct 15 '19 at 08:12
  • Thanks for the reminder! I noted it as they are actually using a closure here – Pim Oct 16 '19 at 16:29
  • The example above uses block-based observer, so it needs to be removed afterwards. – CyberMew Feb 21 '22 at 10:09
2

Check the UIControlEventEditingChanged event...within it, you can set following logic.

Example from this post:

// Add a "textFieldDidChange" notification method to the text field control.
[textField addTarget:self 
              action:@selector(textFieldDidChange:) 
    forControlEvents:UIControlEventEditingChanged];
Community
  • 1
  • 1
ksa_coder
  • 1,393
  • 3
  • 15
  • 38
  • Thanks for the update, but textFieldDidChange is not being called in iOS9 when setting the text (dateTextField.text = "12/01/2015"). – Joe.b Jan 29 '16 at 22:18