Avatar ·

Как закрыть клавиатуру iOS по нажатию в любом месте экрана (Swift)

Всем привет! Я новичок в Swift и iOS разработке. Пишу простое приложение — форму для ввода данных. У меня есть несколько UITextField, и когда пользователь вводит текст, клавиатура появляется нормально. Но я не могу понять, как её убрать, когда пользователь нажимает в любом место экрана (например, на пустое пространство или на фон). Сейчас я сделал так: добавил кнопку "Готово" (Done) на панель аксессуаров клавиатуры, и она работает — клавиатура скрывается. Но это неудобно. Пользователи привыкли, что можно просто тапнуть по фону и клавиатура исчезнет. Я пробовал добавить распознаватель жестов (UITapGestureRecognizer) на главное view, но у меня ничего не работает. Клавиатура не реагирует. Вот мой код: ```swift override func viewDidLoad() { super.viewDidLoad() let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) view.addGestureRecognizer(tap) } @objc func dismissKeyboard() { view.endEditing(true) } ``` Проблема в том, что когда я нажимаю на пустое место, ничего не происходит. Подскажите, пожалуйста, что я делаю не так? Может быть, нужно что-то дополнительно настроить? Заранее спасибо за помощь! <<>>

Решение: корректная настройка UITapGestureRecognizer для скрытия клавиатуры

Ваш код в целом правильный, но есть несколько нюансов, которые могут помешать работе. Самая частая проблема — это то, что UITapGestureRecognizer может конфликтовать с другими жестами (например, с прокруткой в UIScrollView или UITableView). Но в простом случае с формой проблема обычно в другом: распознаватель жестов может не срабатывать, если он не настроен правильно для взаимодействия с другими элементами.

Вот рабочий и самый распространённый подход:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        setupHideKeyboardOnTap()
    }

    private func setupHideKeyboardOnTap() {
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard))
        tapGesture.cancelsTouchesInView = false
        view.addGestureRecognizer(tapGesture)
    }

    @objc private func hideKeyboard() {
        view.endEditing(true)
    }
}

Ключевой момент: свойство cancelsTouchesInView

Обратите внимание на строку tapGesture.cancelsTouchesInView = false. По умолчанию это свойство равно true. Когда вы нажимаете на пустое место, распознаватель жестов "перехватывает" касание и не передаёт его дальше другим элементам (например, кнопкам или другим text field). Установка false позволяет другим элементам интерфейса получать касания после того, как распознаватель обработал жест. Это решает проблему, когда нажатие на фон не срабатывает.

Дополнительные рекомендации:

  • Работа с UIScrollView или UITableView: Если ваша форма находится внутри UIScrollView, добавьте распознаватель на сам scroll view, а не на главное view. Или используйте более продвинутый подход с делегатом UIGestureRecognizerDelegate.
  • Проверка, является ли нажатие на text field: Иногда нужно, чтобы клавиатура не скрывалась, когда пользователь переключается между text field. Для этого можно добавить проверку:
@objc private func hideKeyboard(_ sender: UITapGestureRecognizer) {
    let location = sender.location(in: view)
    if let hitView = view.hitTest(location, with: nil), hitView is UITextField {
        return // не скрываем клавиатуру, если нажали на text field
    }
    view.endEditing(true)
}
  • Альтернативный метод: Используйте resignFirstResponder() для конкретного text field, если у вас только одно поле ввода:
@objc private func hideKeyboard() {
    myTextField.resignFirstResponder()
}

Почему это работает?

Метод view.endEditing(true) рекурсивно проходит по всем subviews и вызывает у каждого resignFirstResponder(). Это гарантирует, что любой активный text field или text view потеряет фокус, и клавиатура закроется.

Это решение подходит для 99% случаев и считается стандартом в iOS разработке на Swift.

<<>>

Альтернативный подход: расширение (extension) для UIViewController

Если вы используете этот функционал во многих контроллерах, удобно вынести его в расширение. Это уменьшит дублирование кода и сделает проект чище.

extension UIViewController {
    func hideKeyboardWhenTappedAround() {
        let tap = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
        tap.cancelsTouchesInView = false
        view.addGestureRecognizer(tap)
    }

    @objc private func dismissKeyboard() {
        view.endEditing(true)
    }
}

Теперь в любом вашем контроллере достаточно вызвать один метод:

override func viewDidLoad() {
    super.viewDidLoad()
    hideKeyboardWhenTappedAround()
}

Дополнение: использование UIResponder

Ещё один способ — использовать цепочку респондеров. Вы можете создать кастомный метод, который будет вызывать resignFirstResponder у текущего активного элемента через UIApplication.shared.sendAction:

@objc private func dismissKeyboard() {
    UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}

Этот метод отправляет действие resignFirstResponder по всей цепочке респондеров, что гарантированно скроет клавиатуру. Однако первый подход с view.endEditing(true) более читаемый и предпочтительный.

Выбирайте тот вариант, который лучше подходит под архитектуру вашего приложения.