Move View with Keyboard Swift: Оптимизация и производительность

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

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

Начать курс

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: Чистим за собой (утечки памяти)

Забывчивость — враг производительности.

Блоги

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