Avatar ·

Как поднять view при появлении клавиатуры в Swift (UITextField и keyboard)

Привет! Я новичок в Swift и UIKit, пишу простое приложение для заметок. Есть экран с UITextField и UITextView внизу. Когда я нажимаю на поле ввода, клавиатура перекрывает нижнюю часть экрана — я не вижу, что печатаю. Пробовал добавлять UIScrollView, но всё равно не работает. Вот пример моего кода (всё в сториборде, outlet подключены): ```swift import UIKit class ViewController: UIViewController { @IBOutlet weak var textField: UITextField! @IBOutlet weak var textView: UITextView! 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) } @objc func keyboardWillShow(notification: NSNotification) { // Тут я попробовал сдвинуть view вверх, но получается криво if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue { self.view.frame.origin.y = -keyboardSize.height } } @objc func keyboardWillHide(notification: NSNotification) { self.view.frame.origin.y = 0 } } ``` Проблема: когда клавиатура появляется, view просто прыгает вверх на всю высоту клавиатуры, даже если текстовое поле находится в середине экрана. Если я нажимаю на другое поле — съезжает ещё больше. Как правильно поднять view именно на высоту клавиатуры минус расстояние до поля? И как сделать анимацию плавной, как в нормальных приложениях? <<>>

Правильное решение: анимация сдвига с учётом позиции текстового поля

Проблема вашего подхода в том, что вы сдвигаете весь 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)

  1. В Xcode: File → Add Packages...
  2. Вставьте URL: https://github.com/hackiftekhar/IQKeyboardManager.git
  3. Выберите версию (последнюю стабильную).

Настройка в 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 КБ).
  • Может конф