no se puede obtener el valor correcto de la altura del teclado en iOS8

83

Estaba usando este código para determinar cuál es el tamaño del teclado:

- (void)keyboardWillChange:(NSNotification *)notification {
    NSDictionary* keyboardInfo = [notification userInfo];
    NSValue* keyboardFrameBegin = [keyboardInfo valueForKey:UIKeyboardFrameBeginUserInfoKey];
    CGRect keyboardFrameBeginRect = [keyboardFrameBegin CGRectValue];

}

Estoy ejecutando esto en el simulador.

El problema es que desde iOS 8 esto no dará el valor correcto, si las sugerencias del teclado están arriba o si las presiono hacia abajo, obtengo valores diferentes (no correctos).

¿Cómo puedo obtener el tamaño exacto del teclado, incluidas las sugerencias de teclado?

Eli Braginskiy
fuente
Podría ayudar si convierte keyboardFrameBeginRecta coordenadas locales.
rmaddy
@rmaddy realmente no importa, solo necesito la altura.
Eli Braginskiy
Lo que podría estar mal dependiendo de la orientación. Aunque eso puede que ya no sea un problema en iOS 8. Pruébelo y vea si hace una diferencia.
rmaddy
@rmaddy Lo probé pero lamentablemente no ayudó
Eli Braginskiy

Respuestas:

98

Utilizar

NSValue* keyboardFrameBegin = [keyboardInfo valueForKey:UIKeyboardFrameEndUserInfoKey];
souvickcse
fuente
Gran respuesta. Gracias. ¿Cómo encontró que debería usar esa clave? @souvickcse
Julian Osorio
6
CGRect keyboardFrame = [keyboardFrameBegin CGRectValue];
Awesomeness
12
No funcionó para mí, el teclado todavía estaba en 258, demasiado alto
marchinram
Gracias @trycatchfinally
souvickcse
1
aprendí una lección hoy. Hay una gran diferencia en UIKeyboardFrameEndUserInfoKey y UIKeyboardFrameBeginUserInfoKey. Gracias @souvickcse
jejernig
120

Con la introducción de teclados personalizados en iOS, este problema se vuelve un poco más complejo.

En resumen, la UIKeyboardWillShowNotification puede ser llamada varias veces por implementaciones de teclado personalizadas:

  1. Cuando se abre el teclado del sistema Apple (en vertical)
    • UIKeyboardWillShowNotification se envía con una altura de teclado de 224
  2. Cuando se abre el teclado Swype (en vertical):
    • UIKeyboardWillShowNotification se envía con una altura de teclado de 0
    • UIKeyboardWillShowNotification se envía con una altura de teclado de 216
    • UIKeyboardWillShowNotification se envía con una altura de teclado de 256
  3. Cuando se abre el teclado SwiftKey (en vertical):
    • UIKeyboardWillShowNotification se envía con una altura de teclado de 0
    • UIKeyboardWillShowNotification se envía con una altura de teclado de 216
    • UIKeyboardWillShowNotification se envía con una altura de teclado de 259

Para manejar estos escenarios correctamente en una línea de código, necesita:

Registre observadores en las notificaciones UIKeyboardWillShowNotification y UIKeyboardWillHideNotification :

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillShow:)
                                             name:UIKeyboardWillShowNotification
                                           object:nil];    
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillHide:)
                                             name:UIKeyboardWillHideNotification
                                           object:nil];

Cree una variable global para rastrear la altura actual del teclado:

CGFloat _currentKeyboardHeight = 0.0f;

Implemente keyboardWillShow para reaccionar al cambio actual en la altura del teclado:

- (void)keyboardWillShow:(NSNotification*)notification {
   NSDictionary *info = [notification userInfo];
   CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
   CGFloat deltaHeight = kbSize.height - _currentKeyboardHeight; 
   // Write code to adjust views accordingly using deltaHeight
   _currentKeyboardHeight = kbSize.height;
}

NOTA: Es posible que desee animar la compensación de vistas. El diccionario de información contiene un valor introducido por UIKeyboardAnimationDurationUserInfoKey . Este valor puede usarse para animar sus cambios a la misma velocidad que el teclado que se muestra.

Implemente keyboardWillOcultar al reinicio _currentKeyboardHeight y reaccionar cuando se descarte el teclado:

- (void)keyboardWillHide:(NSNotification*)notification {
   NSDictionary *info = [notification userInfo];
   CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
   // Write code to adjust views accordingly using kbSize.height
   _currentKeyboardHeight = 0.0f;
}
dgangsta
fuente
dgangsta, su investigación es interesante, pero el problema de la pregunta es simplemente obtener las propiedades FrameBegin en lugar de FrameEnd. De acuerdo con su respuesta, ¿había considerado usar la bandera UIViewAnimationOptionBeginFromCurrentState para el método de vistas de animaciones en lugar de deltas?
MikeR
En lugar de 259 para la altura de SwiftKey (v 1.2.3), obtuve 271 en iPhone6 ​​+ con iOS8.1.3.
Hemang
2
¿Sigue siendo una solución funcional? Cuando uso SwiftKey, obtengo una altura de 44 en lugar de 259.
KIDdAe
Loco, esto me ayudó, pero necesitaba la altura del teclado después de que ya se estaba mostrando, así que observo keyboardDidShow: en su lugar. ¿Por qué estos teclados personalizados activan 3 notificaciones mientras que Apple solo activa una? Parece inconsistente.
Liron Yahdav
Si tiene problemas con el iPhone en modo horizontal como yo, es porque la tecla END del marco tiene el origen incorrecto (al menos para el teclado normal de iOS 10). KEYBOARD BEGIN RECT: (0.0, 375.0, 667.0, 162.0) ... KEYBOARD END RECT: (0.0, 213.0, 667.0, 162.0)
xaphod
18

También tuve este problema, hasta que me encontré con este StackOverflow artículo de :

Convertir UIKeyboardFrameEndUserInfoKey

Esto le muestra cómo usar la convertRectfunción para convertir el tamaño del teclado en algo utilizable, pero en la orientación de la pantalla.

NSDictionary* d = [notification userInfo];
CGRect r = [d[UIKeyboardFrameEndUserInfoKey] CGRectValue];
r = [myView convertRect:r fromView:nil];

Anteriormente, tenía una aplicación para iPad que usaba UIKeyboardFrameEndUserInfoKeypero no usabaconvertRect , y funcionaba bien.

Pero con iOS 8, ya no funcionaba correctamente. De repente, informaría que mi teclado, que se ejecuta en un iPad en modo horizontal, tenía 1024 píxeles de alto .

Así que ahora, con iOS 8, es fundamental que utilice esta convertRectfunción.

Mike Gledhill
fuente
Antes de iOS8, el teclado rect siempre estaba en coordenadas de pantalla orientadas al retrato. Había agregado código para intercambiar manualmente la altura y el ancho cuando estaba en modo horizontal, pero eso se rompió en iOS 8. donde la orientación del teclado recto coincide con la orientación de la vista. La solución convertRect da un resultado correcto para iOS 7 o iOS 8.
user1055568
7

Similar a la solución de dgangsta escrita en Swift 2.0:

override func viewDidLoad() {
    super.viewDidLoad()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
}

deinit {
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

func keyboardWillShow(notification: NSNotification) {
    guard let kbSizeValue = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue else { return }
    guard let kbDurationNumber = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber else { return }
    animateToKeyboardHeight(kbSizeValue.CGRectValue().height, duration: kbDurationNumber.doubleValue)
}

func keyboardWillHide(notification: NSNotification) {
    guard let kbDurationNumber = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber else { return }
    animateToKeyboardHeight(0, duration: kbDurationNumber.doubleValue)
}

func animateToKeyboardHeight(kbHeight: CGFloat, duration: Double) {
    // your custom code here
}
Avt
fuente
problema al mostrar / ocultar quickType, y también el
emoticón
Esta era la salsa. Pequeña diferencia en Swift 2.3, pero es una sugerencia de autocompletar del compilador, por lo que no hay problema.
Ethan Parker
5

Hago extensionparaUIViewController

extension UIViewController {
    func keyboardWillChangeFrameNotification(notification: NSNotification, scrollBottomConstant: NSLayoutConstraint) {
        let duration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber
        let curve = notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber
        let keyboardBeginFrame = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as! NSValue).CGRectValue()
        let keyboardEndFrame = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue()

        let screenHeight = UIScreen.mainScreen().bounds.height
        let isBeginOrEnd = keyboardBeginFrame.origin.y == screenHeight || keyboardEndFrame.origin.y == screenHeight
        let heightOffset = keyboardBeginFrame.origin.y - keyboardEndFrame.origin.y - (isBeginOrEnd ? bottomLayoutGuide.length : 0)

        UIView.animateWithDuration(duration.doubleValue,
            delay: 0,
            options: UIViewAnimationOptions(rawValue: UInt(curve.integerValue << 16)),
            animations: { () in
                scrollBottomConstant.constant = scrollBottomConstant.constant + heightOffset
                self.view.layoutIfNeeded()
            },
            completion: nil
        )
    }
}

Puedes usar así:

override func viewDidLoad() {
    super.viewDidLoad()

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillChangeFrameNotification:", name: UIKeyboardWillChangeFrameNotification, object: nil)
}

...

deinit {
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

func keyboardWillChangeFrameNotification(notification: NSNotification) {
    self.keyboardWillChangeFrameNotification(notification, scrollBottomConstant: inputContainerBottom)
    // Write more to here if you want.
}
Wanbok Choi
fuente
Wanbok Choi, ¿para qué se utilizan las vistas inputContainerBottom?
Nathaniel Blumer
Restricciones de espacio inferior de @Nathaniel UITextField o UITextView desde bottomLayout. ;) Lo había editado en mi proyecto. Lo volveré a publicar
Wanbok Choi
4

Hay ocasiones en las que los desarrolladores necesitan saber la altura del teclado antes de que se muestre, lo que les permite pre-diseñar la interfaz de forma adecuada.

Si ese es el caso, aquí hay una especificación inclusiva:

ingrese la descripción de la imagen aquí

Esto incluye la barra de tipo rápido en la parte superior, ya que está activada de forma predeterminada en todas las versiones actuales de iOS.

Aquí está la configuración de notificación Swift 3 que usé para probar esto, si alguien lo necesita:

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: .UIKeyboardWillShow, object: nil)
}

func keyboardWillShow(notification: NSNotification) {
    guard let keyboardSize = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue else { return }
    print("\(keyboardSize)")
}
Travis M.
fuente
1

Solo una cuerda para veloz:

let keyboardSize = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().size

UIKeyboardFrameEndUserInfoKeysiempre se almacena NSValue, por lo que no es necesario comprobarlo.

Petr Syrov
fuente
0

Noté un problema al cambiar entre el teclado predeterminado y un personalizado (UIPickerView ): el teclado personalizado mostraría una altura de 253 en lugar de 162, después de cambiar desde el teclado predeterminado.

Lo que funcionó en este caso fue la configuración autocorrectionType = UITextAutocorrectionTypeNo;del campo de entrada con el teclado personalizado.

El problema solo ocurrió en iOS 8 (probado solo en el simulador). No ocurre en iOS 9 (simulador o dispositivo).

alex-i
fuente
0

En Swift, no en una sola línea ...

self.keyboardDidShowObserver = NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardDidShowNotification, object: nil, queue: NSOperationQueue.mainQueue(), usingBlock: { (notification) in
        if let userInfo = notification.userInfo, let keyboardFrameValue = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue {
            let keyboardRect = keyboardFrameValue.CGRectValue()
            // keyboardRect.height gives the height of the keyboard
            // your additional code here...
        }
    })
Murray Sagal
fuente
0
[notificationCenter addObserverForName:UIKeyboardWillChangeFrameNotification object:nil queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification * _Nonnull note) {

    float keyboardHeight = [[note.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;

}];
Leo Cavalcante
fuente