Ajustando el contenido ante la aparición/remoción del teclado
Detección de eventos con NotificationCenter
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:
- Se trata de extraer el
framedel teclado como lo vimos antes. Técnicamente esteframeviene 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 usarkeyboardFrameInView. - Se trata de extraer el “first responder” de la aplicación como un
UITextFieldusando el código explicado acá. - El
framedelUITextFieldestá calculado con base en susuperview. Por esta razón, se lo convierte al sistema de coordenadas de la vista con el métodoview.convert(textField.frame, from: textField.superview). - Se extrae el valor de Y del extremo inferior del campo de texto y el valor Y del extremo superior del teclado.
- Se calcula cuántos puntos el teclado está solapado sobre el campo de texto.
- 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