Move View with Keyboard Using Swift: Оптимизация и производительность
Каждый iOS-разработчик хотя бы раз сталкивался с ситуацией, когда клавиатура нагло перекрывает UITextField или UITextView. Пользователь тыкает в поле, клавиатура выезжает, а интерфейс остаётся безучастным. В итоге — раздражение, ошибки ввода и удаление приложения. Решение этой проблемы на Swift — не просто «move view with keyboard», а целая наука о производительности и пользовательском опыте.
В этой статье мы разберём, как правильно реализовать move view with keyboard using Swift, оптимизировать анимации и избежать типичных ошибок. Вы получите готовый код, который можно сразу вставить в проект, и поймёте, почему некоторые подходы тормозят UI, а другие работают идеально.
Почему стандартные решения часто работают плохо?
На Stack Overflow вопрос «Move view with keyboard using Swift» набрал тысячи просмотров. И это не случайно. Многие новички копируют первое попавшееся решение из 2015 года, которое использует UIKeyboardWillShowNotification и жёстко меняет frame у UIView. Результат: дёрганная анимация, утечки памяти и баги на разных моделях iPhone.
Основные проблемы старых подходов:
- Прямое изменение
frameбез учётаsafe area; - Игнорирование длительности и кривых анимации клавиатуры;
- Отсутствие обработки поворота экрана и мультитаскинга;
- Забывчивость: уведомления не удаляются, объекты не очищаются.
Современный Swift (5.0+) и UIKit позволяют сделать всё элегантно и быстро. Давайте разберём оптимальный паттерн.
Подготовка: что нам понадобится
Для реализации move view with keyboard using Swift мы будем использовать:
NotificationCenterдля отслеживания событий клавиатуры;UIView.animateс параметрами из уведомления;UIScrollViewкак базовый контейнер (но можно и без него);- Правильное управление памятью (weak self, removeObserver).
Весь код мы напишем в UIViewController. Если вы используете SwiftUI — принципы те же, но API другой (об этом в конце статьи).
Шаг 1: Регистрируем уведомления клавиатуры
Первое, что нужно сделать — подписаться на два ключевых уведомления: UIResponder.keyboardWillShowNotification и UIResponder.keyboardWillHideNotification. Именно они дают нам размер, длительность и кривую анимации.
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillShow),
name: UIResponder.keyboardWillShowNotification,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillHide),
name: UIResponder.keyboardWillHideNotification,
object: nil
)
}
Обратите внимание: object: nil означает, что мы слушаем уведомления от любой клавиатуры. Это корректно для большинства случаев.
Шаг 2: Обработчик keyboardWillShow
В этом методе мы извлекаем из уведомления размер клавиатуры и длительность анимации. Затем двигаем view вверх на высоту клавиатуры.
@objc private func keyboardWillShow(_ notification: Notification) {
guard let userInfo = notification.userInfo,
let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect,
let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double,
let curve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt else {
return
}
let keyboardHeight = keyboardFrame.height
let animationOptions = UIView.AnimationOptions(rawValue: curve)
UIView.animate(
withDuration: duration,
delay: 0,
options: animationOptions,
animations: { [weak self] in
self?.view.frame.origin.y = -keyboardHeight
},
completion: nil
)
}
Ключевые моменты:
- weak self — предотвращает retain cycle, так как замыкание захватывает self;
- Используем duration и curve из уведомления — тогда анимация будет синхронизирована с системной клавиатурой, без рывков;
- Не хардкодим высоту — на разных устройствах и при разных языках клавиатура может быть разной высоты.
Шаг 3: Обработчик keyboardWillHide
Когда клавиатура убирается, возвращаем view в исходное положение. Аналогично используем параметры из уведомления.
@objc private func keyboardWillHide(_ notification: Notification) {
guard let userInfo = notification.userInfo,
let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double,
let curve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt else {
return
}
let animationOptions = UIView.AnimationOptions(rawValue: curve)
UIView.animate(
withDuration: duration,
delay: 0,
options: animationOptions,
animations: { [weak self] in
self?.view.frame.origin.y = 0
},
completion: nil
)
}
Всё просто: сдвигаем frame обратно. Но есть нюанс: если у вас несколько текстовых полей, такой подход сдвинет весь экран, а не только нужную область. Для точного позиционирования лучше использовать UIScrollView.
Шаг 4: Оптимизация с UIScrollView
Если ваша форма ввода данных находится в UIScrollView, то двигать весь view неправильно. Вместо этого мы изменяем contentInset scroll view, чтобы контент прокручивался выше клавиатуры.
@objc private func keyboardWillShow(_ notification: Notification) {
guard let userInfo = notification.userInfo,
let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect,
let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double,
let curve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt else {
return
}
let keyboardHeight = keyboardFrame.height - view.safeAreaInsets.bottom
let animationOptions = UIView.AnimationOptions(rawValue: curve)
UIView.animate(
withDuration: duration,
delay: 0,
options: animationOptions,
animations: { [weak self] in
self?.scrollView.contentInset.bottom = keyboardHeight
self?.scrollView.verticalScrollIndicatorInsets.bottom = keyboardHeight
},
completion: nil
)
}
Почему это быстрее?
- Не пересчитывается layout всего view;
- ScrollView использует внутреннюю оптимизацию Core Animation;
- Работает с любым количеством полей ввода.
Шаг 5: Чистим за собой (утечки памяти)
Забывчивость — враг производительности.