UITextField text change event: ловим каждое нажатие правильно

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

UITextField text change event: каждый символ на счету

Представьте: вы пишете приложение для заметок, поиск по каталогу или мессенджер. Пользователь только начинает печатать в UITextField, а вам уже нужно реагировать — подсвечивать подсказки, считать оставшиеся символы или отправлять запрос на сервер. Как поймать этот самый момент, когда текст изменился? Вопрос, который мучает новичков и даже опытных iOS-разработчиков. На Stack Overflow тема UITextField text change event собрала тысячи просмотров — и неспроста.

В iOS разработке есть как минимум три рабочих подхода. И выбор неправильного может стоить вам багов, утечки памяти или лишних 100 строк кода. Сегодня мы разложим по полочкам все способы отследить изменение текста в UITextField: от классического делегата до современного Combine. Поехали!

Почему недостаточно просто нажать кнопку?

Казалось бы, что сложного? Пользователь ввел текст — нажал «Готово» или кнопку «Поиск». Но современные интерфейсы требуют реакции в реальном времени. Вот типичные сценарии:

  • Автодополнение (search as you type) — подсказки появляются при каждом нажатии клавиши.
  • Валидация на лету — проверка email или номера телефона без отправки формы.
  • Счетчик символов — например, в Twitter-клиенте нужно показывать, сколько букв осталось до лимита.
  • Динамический поиск — фильтрация списка контактов или товаров.

Во всех этих случаях нам нужно событие UITextField text change event. И теперь вопрос: как его правильно реализовать в Swift?

Способ 1: Делегат UITextFieldDelegate — классика жанра

Самый очевидный и, пожалуй, самый популярный метод. Вы назначаете делегата своему текстовому полю и реализуете метод textField(_:shouldChangeCharactersIn:replacementString:). Но есть нюанс: этот метод вызывается до того, как текст реально изменится. Он спрашивает: «Можно ли вставить этот символ?»

Чтобы получить актуальный текст, нужно сконструировать его вручную:

class ViewController: UIViewController, UITextFieldDelegate {
    
    @IBOutlet weak var textField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        textField.delegate = self
    }
    
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        let currentText = textField.text ?? ""
        guard let stringRange = Range(range, in: currentText) else { return false }
        let updatedText = currentText.replacingCharacters(in: stringRange, with: string)
        
        print("Новый текст: \(updatedText)")
        // Здесь можно обновить UI или запустить поиск
        return true
    }
}

Плюсы:

  • Полный контроль над вводом — можно запретить вставку определенных символов.
  • Работает на всех версиях iOS.

Минусы:

  • Код громоздкий для простого «поймать изменение».
  • Нужно помнить про NSRange и конвертацию в Range<String.Index>.
  • Не срабатывает при программном изменении textField.text = "новый текст".

Этот подход отлично подходит, если вам нужно валидировать ввод (например, запретить буквы в поле для номера телефона). Но для простого отслеживания изменений есть более элегантные варианты.

Способ 2: Target-Action (UIControl.Event.editingChanged) — простота и надежность

Второй способ — это добавление цели и действия для события .editingChanged. Он срабатывает после каждого изменения текста, и код выглядит лаконично:

class ViewController: UIViewController {
    
    @IBOutlet weak var textField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
    }
    
    @objc func textFieldDidChange(_ textField: UITextField) {
        guard let text = textField.text else { return }
        print("Текст изменился: \(text)")
        // Обновляем поиск, счетчик и т.д.
    }
}

Можно сделать даже проще — подключить @IBAction прямо из Storyboard:

  1. Откройте Assistant Editor.
  2. Зажмите Ctrl и перетащите из Value Changed события текстового поля в код.
  3. Выберите тип UITextField и назовите метод.

И вуаля — код сгенерируется автоматически.

Плюсы:

  • Минимум кода, легко читается.
  • Не нужно возиться с делегатами и NSRange.
  • Срабатывает при любом изменении, включая программное (если вызвать sendActions(for: .editingChanged)).

Минусы:

  • Не дает отменить ввод (не подходит для валидации).
  • Требует @objc аннотации.

Этот способ — золотая середина для 80% задач. Если вам нужно просто узнать, что UITextField text change event произошел — используйте .editingChanged.

Способ 3: Уведомления (NotificationCenter) — для сложной архитектуры

Иногда один контроллер должен знать об изменениях в другом. Или вам нужно реагировать на изменения текста в нескольких полях сразу. Тут на помощь приходит UITextField.textDidChangeNotification:

class ViewController: UIViewController {
    
    @IBOutlet weak var textField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(textDidChange(_:)),
            name: UITextField.textDidChangeNotification,
            object: textField
        )
    }
    
    @objc func textDidChange(_ notification: Notification) {
        guard let textField = notification.object as? UITextField,
              let text = textField.text else { return }
        print("Уведомление: текст = \(text)")
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

Плюсы:

  • Развязывает код — можно подписаться из любого места.
  • Удобно для глобальных обработчиков.

Минусы:

  • Нужно не забыть отписаться (deinit), иначе — утечка памяти.
  • Меньше контроля, чем у делегата.

Используйте уведомления, если вы строите архитектуру на MVVM или Coordinator, где ViewModel не должна иметь

Блоги

Рекомендаций по книгам