← Back to list

Ajustando el contenido ante la aparición/remoción del teclado

Detección de eventos con NotificationCenter

GoyesDev · 2025-09-10 19:14 · 0 claps · 3.3 min read
#uiscrollview #teclado #notification-center
Open on Medium ↗

Ajustando el contenido ante la aparición/remoción del teclado

Detección de eventos con NotificationCenter

Descripción del problema

Cuando aparece el teclado en la pantalla, es posible que se oculte parte del contenido de la vista. Por esta razón, es necesario desplazarla hacia arriba o, en caso de tener un UIScrollView, aumentar el margen inferior para que se pueda desplazar el contenido encima del teclado.

Detectar aparición/remoción del teclado

Es necesario suscribirse a los eventos .keyboardWillHideNotification y .keyboardWillShowNotification del UIResponder, por medio del NotificationCenter. Una vez que se detecten esos eventos, se dispara el método adjustForKeyboard:

private func registerForKeyboardChanges() {
    let notificationCenter = NotificationCenter.default
    notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillHideNotification, object: nil)
    notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillShowNotification, object: nil)
//    notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
}

Extraer el tamaño del teclado

Al procesar la notificación, se puede extraer el tamaño del teclado del atributo keyboardFrameEndUserInfoKey que viene en el userInfo:

@objc private func adjustForKeyboard(notification: Notification) {
    guard let keyboardValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
    let keyboardScreenEndFrame = keyboardValue.cgRectValue
}

Normalizar sistema de coordenadas

El tamaño del teclado viene dado relativo al sistema de coordenadas de la ventana. Como en la ventana podríamos tener pestañas, safeAreas, o una barra de navegación, es necesario normalizar el tamaño del teclado al sistema de coordenadas de la vista actual. Para esto se usa el método convert:from::

let keyboardViewEndFrame = convert(keyboardScreenEndFrame, from: window)

Agregar margen inferior al contenido

Se mueve el contenido hacia arriba, sobre el teclado, al agregar un margen en el lado inferior de la vista (o del UIScrollView) que sea tan grande como tamaño del teclado:

if notification.name == UIResponder.keyboardWillHideNotification {
    scrollView.contentInset = .zero
} else {
    scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardViewEndFrame.height - safeAreaInsets.bottom, right: 0)
}

Agregar margen inferior a la barra indicadora de desplazamiento

La barra de scroll también deben respetar el espacio del teclado. Para esto, debemos poner un margen también:

scrollView.scrollIndicatorInsets = scrollView.contentInset

Método completo

Esta es toda la implementación requerida para mover el contenido de la vista ante la aparición/remoción del teclado:

private func registerForKeyboardChanges() {
    let notificationCenter = NotificationCenter.default
    notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillHideNotification, object: nil)
    notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillShowNotification, object: nil)
    notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
}
@objc private func adjustForKeyboard(notification: Notification) {
    guard let keyboardValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
    let keyboardScreenEndFrame = keyboardValue.cgRectValue
    let keyboardViewEndFrame = convert(keyboardScreenEndFrame, from: window)
    if notification.name == UIResponder.keyboardWillHideNotification {
        scrollView.contentInset = .zero
    } else {
        scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardViewEndFrame.height - safeAreaInsets.bottom, right: 0)
    }
    scrollView.scrollIndicatorInsets = scrollView.contentInset
}

Ocultar el teclado cuando se pulsa fuera del campo de texto

Al pulsar fuera de un campo de texto, se debe dejar de editar la vista o renunciar a ser el “first responder”. Para ello, hay que agregar un UITapGestureRecognizer a la vista que contiene el formulario y luego invocar el método endEditingpara dejar de editar:

private func registerTapGestureRecognizer() {
    let tapBackground = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
    tapBackground.numberOfTapsRequired = 1
    addGestureRecognizer(tapBackground)
}
@objc private func dismissKeyboard() {
    endEditing(true)
}

Verificar si el cursor está debajo del teclado

En ocasiones puede ser que no se tenga un UIScrollView sino que se quiere desplazar el contenido de la vista solo si el teclado aparece encima de un campo de texto seleccionado. En este caso, se puede usar el siguiente código:

@objc private func keyboardWillShow(sender: NSNotification) {
    guard let userInfo = sender.userInfo, let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue, // 1
       let currentTextField = UIResponder.currentFirst() as? UITextField else { // 2
              return
    }
    // let keyboardFrameInView = view.convert(keyboardFrame, from: nil)
    // 3
    let convertedTextFieldFrame = view.convert(currentTextField.frame, from: currentTextField.superview)
    // 4
    let textFieldBottomY = convertedTextFieldFrame.maxY
    let keyboardTopY = keyboardFrame.cgRectValue.origin.y
    // 5
    let overlap = textFieldBottomY - keyboardTopY

    // 6
    if overlap > 0 {
      // Ajustar la vista...
    }
}

En el código anterior estaba pasando lo siguiente:

  1. Se trata de extraer el frame del teclado como lo vimos antes. Técnicamente este frame viene en el sistema de coordenadas de la pantalla y lo ideal es convertirlo al sistema de coordenadas de la ventana como también vimos anteriormente. Por esta razón, se puede usar keyboardFrameInView.
  2. Se trata de extraer el “first responder” de la aplicación como un UITextField usando el código explicado acá.
  3. El frame del UITextField está calculado con base en su superview. Por esta razón, se lo convierte al sistema de coordenadas de la vista con el método view.convert(textField.frame, from: textField.superview) .
  4. Se extrae el valor de Y del extremo inferior del campo de texto y el valor Y del extremo superior del teclado.
  5. Se calcula cuántos puntos el teclado está solapado sobre el campo de texto.
  6. Si el teclado alcanza a estar encima del campo de texto entonces se ajusta la vista.

Mover la vista apenas lo suficiente para que no estorbe el teclado

Probar con el siguiente código si el borde superior del teclado está por encima del borde inferior del campo de texto (i.e. textFieldBottomY > keyboardTopY) :

let overlap = textFieldFrame.maxY - keyboardFrameInView.origin.y
if overlap > 0 {
    UIView.animate(withDuration: 0.3) {
        self.view.frame.origin.y = -overlap - 10 // 10px de margen
    }
}

Donde se mueve la vista hacia arriba lo suficiente como para cubrir el solapamiento más 10 puntos más de margen. Aparte, usar una animación resulta ser muy elegante.


메타데이터
post_id
d7f080f951e3
slug
moviendo-un-uiscrollview-ante-la-aparición-remoción-del-teclado-d7f080f951e3
url
https://medium.com/@luis.goyes117/moviendo-un-uiscrollview-ante-la-aparici%C3%B3n-remoci%C3%B3n-del-teclado-d7f080f951e3
canonical_url
https://medium.com/@luis.goyes117/moviendo-un-uiscrollview-ante-la-aparici%C3%B3n-remoci%C3%B3n-del-teclado-d7f080f951e3
author_url
https://medium.com/@luis.goyes117
status
ok
fetched_at
2026-07-15 17:30:05