UITextField text change event: Every Character Counts
\n\nImagine: you're writing a notes app, a catalog search, or a messenger. The user just starts typing in a UITextField, and you already need to react — highlight suggestions, count remaining characters, or send a request to the server. How do you catch that exact moment when the text changes? A question that plagues beginners and even experienced iOS developers. On Stack Overflow, the topic UITextField text change event has gathered thousands of views — and for good reason.
In iOS development, there are at least three working approaches. Choosing the wrong one could cost you bugs, memory leaks, or an extra 100 lines of code. Today, we'll break down all the ways to track text changes in UITextField: from the classic delegate to modern Combine. Let's go!
\n\nWhy Isn't Just Pressing a Button Enough?
\n\nIt seems simple, right? The user enters text — presses "Done" or the "Search" button. But modern interfaces require real-time reactions. Here are typical scenarios:
\n- \n
- Autocomplete (search as you type) — suggestions appear with every keystroke. \n
- Live validation — checking an email or phone number without submitting a form. \n
- Character counter — for example, in a Twitter client, showing how many characters are left until the limit. \n
- Dynamic search — filtering a list of contacts or products. \n
In all these cases, we need the UITextField text change event. And now the question: how to implement it correctly in Swift?
Method 1: UITextFieldDelegate — The Classic Approach
\n\nThe most obvious and perhaps the most popular method. You assign a delegate to your text field and implement the textField(_:shouldChangeCharactersIn:replacementString:) method. But there's a nuance: this method is called before the text actually changes. It asks: "Can I insert this character?"
To get the actual text, you need to construct it manually:
\n\nclass ViewController: UIViewController, UITextFieldDelegate {\n \n @IBOutlet weak var textField: UITextField!\n \n override func viewDidLoad() {\n super.viewDidLoad()\n textField.delegate = self\n }\n \n func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {\n let currentText = textField.text ?? ""\n guard let stringRange = Range(range, in: currentText) else { return false }\n let updatedText = currentText.replacingCharacters(in: stringRange, with: string)\n \n print("New text: \\(updatedText)")\n // Here you can update the UI or start a search\n return true\n }\n}\n\n\nPros:
\n- \n
- Full control over input — you can prohibit inserting certain characters. \n
- Works on all iOS versions. \n
Cons:
\n- \n
- Code is cumbersome for simply "catching a change". \n
- You need to remember about
NSRangeand conversion toRange<String.Index>. \n - Does not trigger when changing text programmatically with
textField.text = "new text". \n
This approach is great if you need to validate input (for example, prohibit letters in a phone number field). But for simple change tracking, there are more elegant options.
\n\nMethod 2: Target-Action (UIControl.Event.editingChanged) — Simplicity and Reliability
\n\nThe second method is adding a target and action for the .editingChanged event. It triggers after each text change, and the code looks concise: