При нажатии на UITextField или UITextView в iOS-приложении клавиатура автоматически появляется, но часто требуется скрыть её программно — например, после завершения ввода, при нажатии на фон или по кнопке "Готово". Стандартное поведение SwiftUI и UIKit не всегда интуитивно: если просто вызвать resignFirstResponder() на неактивном поле, клавиатура останется на экране, а при неправильной обработке жестов приложение может крашиться с ошибкой unrecognized selector.
Проблема усложняется в iOS 17+, где Apple изменила логику отображения клавиатуры для UIScrollView и модальных окон. Например, если скрывать клавиатуру в viewDidLoad(), это сработает только для первого поля ввода, а для динамически создаваемых элементов потребуется отдельная обработка. В этой статье разберём 5 надёжных способов скрытия клавиатуры — от базовых до продвинутых, с учётом особенностей Swift 5.9 и последних версий Xcode.
1. Базовый метод: resignFirstResponder()
Самый простой способ скрыть клавиатуру — вызвать метод resignFirstResponder() на активном текстовом поле. Этот метод сообщает системе, что поле больше не является "первым респондером" (т.е. не принимает ввод с клавиатуры). Однако здесь есть нюансы:
- 🔹 Работает только если поле уже активно (т.е. клавиатура отображается).
- 🔹 Не скрывает клавиатуру, если поле не в фокусе (например, после перехода на другой экран).
- 🔹 В SwiftUI требует обёртки в
UIHostingController.
Пример для UIKit:
@IBOutlet weak var textField: UITextField!
// Скрыть клавиатуру по нажатию на кнопку
@IBAction func hideKeyboard(_ sender: UIButton) {
textField.resignFirstResponder()
}
Для динамически создаваемых полей (например, в UITableView) нужно хранить ссылку на активное поле или использовать UIResponder цепочку:
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
view.endEditing(true) // Скрывает клавиатуру для всех полей во view
}
2. Скрытие клавиатуры по тапу на фон (UITapGestureRecognizer)
Частая задача — скрывать клавиатуру при тапе на любую область экрана, кроме полей ввода. Для этого добавляем UITapGestureRecognizer к корневому UIView. Важно настроить делегата жеста, чтобы он не конфликтовал с другими интерактивными элементами (например, кнопками или UICollectionView).
Пример с настройкой в viewDidLoad():
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideKeyboardOnTap))
tapGesture.cancelsTouchesInView = false // Разрешает обработку тапов вложенными view
view.addGestureRecognizer(tapGesture)
}
@objc func hideKeyboardOnTap() {
view.endEditing(true)
}
- 📱 Подходит для экранов с множеством полей ввода (например, формы регистрации).
- ⚠️ Не работает, если поверх
UIViewлежит модальное окно илиUIAlertController. - 🔧 Для SwiftUI используйте модификатор
.onTapGesture { UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) }.
3. Отслеживание событий клавиатуры через NotificationCenter
Если нужно скрыть клавиатуру при определённых событиях (например, при скролле или смене ориентации экрана), подпишитесь на уведомления UIKeyboardWillHideNotification и UIKeyboardDidHideNotification. Это полезно для сложных интерфейсов, где клавиатура должна скрываться автоматически.
Пример кода:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillHide),
name: UIResponder.keyboardWillHideNotification,
object: nil
)
}
@objc func keyboardWillHide(notification: NSNotification) {
// Дополнительная логика (например, скролл к началу экрана)
print("Клавиатура скрывается")
}
deinit {
NotificationCenter.default.removeObserver(self)
}
| Уведомление | Когда вызывается | Типичное использование |
|---|---|---|
UIKeyboardWillShowNotification |
Перед появлением клавиатуры | Сдвиг контента вверх |
UIKeyboardDidShowNotification |
После появления клавиатуры | Фиксация положения скролла |
UIKeyboardWillHideNotification |
Перед скрытием клавиатуры | Восстановление исходного положения view |
Важно: В iOS 15+ Apple рекомендует использовать UIKeyboardLayoutGuide вместо ручной обработки уведомлений для адаптивного интерфейса.
4. Продвинутые решения: IQKeyboardManager и SwiftUI
Для крупных проектов с десятками экранов ручная обработка клавиатуры неэффективна. В этом случае используют сторонние библиотеки или встроенные инструменты SwiftUI.
4.1. IQKeyboardManager
Библиотека IQKeyboardManager автоматически управляет клавиатурой: скрывает её по тапу на фон, адаптирует UIScrollView и поддерживает кастомные toolbar’ы. Установка через CocoaPods:
pod 'IQKeyboardManagerSwift'
Минимальная настройка в AppDelegate:
import IQKeyboardManagerSwift
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
IQKeyboardManager.shared.enable = true
return true
}
}
4.2. SwiftUI: ключевые модификаторы
В SwiftUI клавиатура управляется через модификаторы:
- 🔧
.keyboardType(.emailAddress)— настройка типа клавиатуры. - 🔧
.submitLabel(.done)— изменение текста кнопки "Готово". - 🔧
.onSubmit { hideKeyboard() }— действие при нажатии "Готово".
Пример скрытия клавиатуры в SwiftUI:
struct ContentView: View {
@State private var text = ""
@FocusState private var isFocused: Bool
var body: some View {
TextField("Введите текст", text: $text)
.focused($isFocused)
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Spacer()
Button("Готово") {
isFocused = false // Скрывает клавиатуру
}
}
}
}
}
☑️ Проверка перед использованием IQKeyboardManager
5. Распространённые ошибки и их решения
Даже опытные разработчики сталкиваются с проблемами при работе с клавиатурой. Вот типичные ошибки и способы их исправления:
⚠️ Внимание: Если клавиатура не скрывается после вызова resignFirstResponder(), проверьте, не блокирует ли её модальное окно или алерт. В этом случае сначала закройте алерт, затем скрывайте клавиатуру.
| Ошибка | Причина | Решение |
|---|---|---|
| Клавиатура не скрывается при тапе на фон | Жест UITapGestureRecognizer не добавлен или перекрыт другим view |
Проверьте порядок добавления жестов (view.addGestureRecognizer должен быть после инициализации дочерних view) |
Крэш при вызове resignFirstResponder() |
Поле ввода (UITextField) равно nil или уже деинициализировано |
Используйте view.endEditing(true) вместо прямого вызова |
| Клавиатура появляется снова после скрытия | Автофокус на другое поле (например, в UITableView) |
Отключите автофокус или обработайте переход между полями |
Ещё одна частая проблема — клавиатура не скрывается в UIScrollView при прокрутке. Решение:
scrollView.keyboardDismissMode = .interactive // или .onDrag
6. Оптимизация для разных версий iOS
Поведение клавиатуры менялось в разных версиях iOS. Например, в iOS 13 появился UIKeyboardLayoutGuide, а в iOS 16 — новые модификаторы для SwiftUI. Чтобы код работал универсально, используйте проверки версии:
Пример адаптивного кода:
if #available(iOS 15.0, *) {
// Используем UIKeyboardLayoutGuide для iOS 15+
let guide = view.keyboardLayoutGuide
someConstraint = view.bottomAnchor.constraint(equalTo: guide.topAnchor)
} else {
// Старый способ для iOS 14 и ниже
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillShow),
name: UIResponder.keyboardWillShowNotification,
object: nil
)
}
Для SwiftUI в iOS 16+ доступен новый модификатор .scrollDismissalBehavior, который автоматически скрывает клавиатуру при скролле:
ScrollView {
// Контент
}
.scrollDismissalBehavior(.immediately)
Как тестировать скрытие клавиатуры на симуляторе
1. Запустите приложение в Xcode симуляторе (Cmd + R).
2. Нажмите на текстовое поле, чтобы вызвать клавиатуру.
3. Для имитации тапа на фон используйте мышь или трекпад.
4. Чтобы проверить скрытие по кнопке "Готово", нажмите Command + K в симуляторе (это эквивалент нажатия на клавиатуре).
5. Для тестирования скролла используйте мышь или жесты трекпада.
6. Проверьте логи в консоли Xcode (Cmd + Shift + C) на наличие ошибок типа "unrecognized selector".
7. Альтернативные подходы: кастомные клавиатуры и Input Accessory View
Если стандартные методы не подходят, можно создать кастомную клавиатуру или использовать inputAccessoryView. Это полезно для:
- 🎨 Некстандартного дизайна (например, клавиатура с эмодзи).
- 🔒 Дополнительных кнопок (сканирование QR, вставка шаблонов).
- 📱 Оптимизации под специфичные задачи (например, калькулятор в банковских приложениях).
Пример добавления inputAccessoryView:
let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 50))
toolbar.barStyle = .default
let doneButton = UIBarButtonItem(title: "Готово", style: .done, target: self, action: #selector(doneButtonTapped))
toolbar.items = [doneButton]
toolbar.sizeToFit()
textField.inputAccessoryView = toolbar
@objc func doneButtonTapped() {
textField.resignFirstResponder()
}
Для полной кастомизации клавиатуры потребуется создать subclass UIInputViewController и настроить его в Info.plist:
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>UIKeyboardHasFullAccess</key>
<false/>
</dict>
</dict>
FAQ: Частые вопросы по скрытию клавиатуры в Swift
Почему клавиатура не скрывается после вызова resignFirstResponder()?
Вероятные причины:
- Поле ввода не является первым респондером (проверьте через
textField.isFirstResponder). - Клавиатура принадлежит другому полю (например, в
UITableViewс несколькими ячейками). - Конфликт с анимацией или модальным окном (попробуйте
view.endEditing(true)).
Решение: используйте UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) для скрытия клавиатуры глобально.
Как скрыть клавиатуру при прокрутке UIScrollView?
Начиная с iOS 10, можно использовать свойство keyboardDismissMode:
scrollView.keyboardDismissMode = .interactive // или .onDrag
Для более ранних версий iOS добавьте обработчик жеста скролла:
scrollView.panGestureRecognizer.addTarget(self, action: #selector(handleScroll))
Можно ли скрыть клавиатуру анимацией?
Да, для этого используйте UIView.animate вместе с resignFirstResponder():
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
self.textField.resignFirstResponder()
}
Однако учтите, что сама клавиатура скрывается с системной анимацией, которую нельзя переопределить.
Как скрыть клавиатуру в SwiftUI при нажатии на кнопку?
Используйте @FocusState:
struct ContentView: View {
@State private var text = ""
@FocusState private var isFocused: Bool
var body: some View {
VStack {
TextField("Текст", text: $text)
.focused($isFocused)
Button("Скрыть клавиатуру") {
isFocused = false
}
}
}
}
Почему клавиатура появляется снова после поворота экрана?
Это происходит из-за автоматического восстановления фокуса системой. Решения:
- Отключите автофокус в
viewWillTransition(to:with:). - Сохраните состояние фокуса перед поворотом и восстановите его после.
- Используйте
UIKeyboardWillHideNotificationдля принудительного скрытия.