Как поднять view при появлении клавиатуры в Swift (UITextField и keyboard)
Правильное решение: анимация сдвига с учётом позиции текстового поля
Проблема вашего подхода в том, что вы сдвигаете весь view на фиксированную высоту клавиатуры, не учитывая, где находится активное текстовое поле. Правильное решение — использовать UIScrollView или изменять contentInset у существующего scroll view. Вот рабочий и самый популярный способ.
Шаг 1: Добавьте UIScrollView в storyboard или кодом
Оберните все ваши элементы (textField, textView и т.д.) в UIScrollView. Это стандартная практика для экранов с клавиатурой.
Шаг 2: Подписка на уведомления (уже почти правильно)
Вы уже подписались, но нужно добавить отписку и использовать правильные селекторы с параметром уведомления.
Шаг 3: Полный код решения
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
setupKeyboardNotifications()
}
deinit {
// Важно: отписываемся от уведомлений, чтобы избежать утечек памяти
NotificationCenter.default.removeObserver(self)
}
private func setupKeyboardNotifications() {
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillShow),
name: UIResponder.keyboardWillShowNotification,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillHide),
name: UIResponder.keyboardWillHideNotification,
object: nil
)
}
@objc private func keyboardWillShow(_ notification: Notification) {
guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue,
let keyboardDuration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double,
let keyboardCurve = notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt else {
return
}
let keyboardHeight = keyboardFrame.cgRectValue.height
let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardHeight, right: 0)
// Анимируем изменение inset вместе с клавиатурой
UIView.animate(withDuration: keyboardDuration,
delay: 0,
options: UIView.AnimationOptions(rawValue: keyboardCurve)) {
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
}
// Дополнительно: скроллим к активному полю ввода
if let activeField = findActiveField() {
let rect = activeField.convert(activeField.bounds, to: scrollView)
scrollView.scrollRectToVisible(rect, animated: false)
}
}
@objc private func keyboardWillHide(_ notification: Notification) {
guard let keyboardDuration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double,
let keyboardCurve = notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt else {
return
}
let contentInsets = UIEdgeInsets.zero
UIView.animate(withDuration: keyboardDuration,
delay: 0,
options: UIView.AnimationOptions(rawValue: keyboardCurve)) {
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
}
}
// Вспомогательный метод для поиска активного поля
private func findActiveField() -> UIView? {
if textField.isFirstResponder { return textField }
if textView.isFirstResponder { return textView }
return nil
}
}
Объяснение ключевых моментов:
- contentInset — добавляет отступ снизу, равный высоте клавиатуры, чтобы содержимое scroll view не перекрывалось.
- scrollIndicatorInsets — синхронизирует индикаторы прокрутки с новыми отступами.
- Анимация — используем те же duration и curve, что и у клавиатуры (берём из userInfo), чтобы движение было плавным и синхронным.
- scrollRectToVisible — автоматически прокручивает к активному полю, если оно скрыто клавиатурой.
- Отписка в deinit — обязательна, иначе приложение может упасть с ошибкой, если контроллер уничтожен, а уведомление пришло.
Альтернативный простой вариант (без UIScrollView)
Если у вас простой экран и лень оборачивать в scroll view, можно сдвигать constraint нижней части view:
@IBOutlet weak var bottomConstraint: NSLayoutConstraint! // Привязка нижнего элемента к Safe Area
@objc func keyboardWillShow(_ notification: Notification) {
guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
let keyboardHeight = keyboardFrame.cgRectValue.height
bottomConstraint.constant = keyboardHeight - view.safeAreaInsets.bottom
UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() }
}
@objc func keyboardWillHide(_ notification: Notification) {
bottomConstraint.constant = 0
UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() }
}
Этот способ проще, но менее гибкий — работает только если у вас один элемент внизу.
<<Использование библиотеки IQKeyboardManager (быстрый вариант)
Если вы не хотите писать код вручную и предпочитаете готовое решение, существует популярная библиотека IQKeyboardManager. Она автоматически управляет клавиатурой для всех текстовых полей во всём приложении — без единой строчки кода в контроллерах.
Установка через Swift Package Manager (SPM)
- В Xcode: File → Add Packages...
- Вставьте URL:
https://github.com/hackiftekhar/IQKeyboardManager.git - Выберите версию (последнюю стабильную).
Настройка в AppDelegate
import IQKeyboardManagerSwift
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
IQKeyboardManager.shared.enable = true
IQKeyboardManager.shared.shouldResignOnTouchOutside = true // Закрытие клавиатуры по тапу вне поля
return true
}
}
Преимущества:
- Работает из коробки — не нужно писать уведомления или менять constraints.
- Поддерживает
UITextField,UITextView,UISearchBarи даже кастомные view. - Автоматически поднимает view, скроллит к полю, показывает/скрывает клавиатуру.
- Есть кастомизация: расстояние между полем и клавиатурой, анимация, работа с toolbars.
Недостатки:
- Добавляет зависимость в проект (около 200 КБ).
- Может конф