¿Manejando UIKeyboardWillShowNotification para que el keyboard ya esté encendido cuando se empuje un controller de vista?

Normalmente, si configura un UITextField para convertirse en el primer respondedor en viewDidLoad o viewWillAppear , cuando el controller de vista se viewWillAppear en la stack de navigation, obtendrá una animation donde el controller de vista se desliza desde la derecha con el keyboard ya arriba (es decir, no hay doble animation):

introduzca la descripción de la imagen aquí
Animación ideal (250 ms)

Sin embargo, cuando uso el layout automático y controlo el layout de mi vista según el tamaño del keyboard , causa animaciones extrañas. En este caso, mi button tiene una restricción en la parte inferior de la supervisión y actualizo su propiedad constante en el evento de notificación del keyboard.

introduzca la descripción de la imagen aquí
Animaciones Buggy (250 ms)

(Observe cómo el button, el marcador de position y la label se animan hacia arriba durante el push. Observe también cómo el cursor comienza debajo del text del marcador de position y flota hacia arriba hasta que llegue al marcador de position).

El problema subyacente parece ser que cuando la notificación se activa, ya está en un bloque de animation (el que anima el controller de navigation), luego self.view.layoutIfNeeded() otro bloque de animation dentro del bloque existente (es decir, self.view.layoutIfNeeded() por lo que los cambios de layout automático están animados ).

Por supuesto que podría llamar a becomeFirstResponder en viewDidAppear , pero luego obtengo dos animaciones. Quiero utilizar el comportamiento del keyboard ya existente para que la interfaz parezca más receptiva (lo ideal es mantenerla cerca de 250 ms ).

introduzca la descripción de la imagen aquí
Animación doble (600 ms)

¿Cómo puedo manejar el UIKeyboardWillShowNotification de tal manera que funcione tanto cuando el usuario toca un UITextField y, al presionar un controller de vista, usa mi animation ideal arriba?


Solución ideal:

  • Apple proporcionaría otro valor en el dictionary de notificación, como shouldAnimate, que le indicaría si debe usar un locking UIAnimation o no al manejar la notificación. Ej: en el caso de este impulso, enviará falso , pero si el usuario simplemente toca un campo de text, enviará verdadero .

Hacky soluciones:

  • En lugar de configurar immediatelyFirstReponder en viewDidLoad de inmediato, llámelo con un retardo arbitrario. Esto provocará una doble animation indeseable: primero, el controller de visualización se deslizará desde la derecha y luego aparecerá el keyboard. (Vea la animation de la animation doble arriba para un ejemplo de esto).
  • Deduzca si está en un bloque de animation mirando una key en el dictionary de información de usuario de la notificación, como UIKeyboardAnimationDurationUserInfoKey que es 0.35 durante un push y 0.25 cuando el usuario toca un UITextField. Esto puede romperse fácilmente si las animaciones están personalizadas, Apple cambia las animaciones, etc.
  • Establezca un indicador en viewWillAppear ( preventAnimationBlock = true ) y viewDidAppear ( preventAnimationBlock = false ). Esto parece ser el enfoque más seguro y produce la animation exacta que deseo. Sin embargo, mantener el estado es indeseable.

Soluciones que no funcionaron:

  • Diseño en viewDidLoad . Al final de la vista, la carga, la llamada setNeedsLayout y el layoutIfNeeded , mientras que esta solución funcionó en el pasado, no funciona con el ejemplo del button anterior. Hará que la label y la vista de text no se animen correctamente, pero el button seguirá animándose incorrectamente.
  • Intente verificar si ya está en el bloque de animation : si hubiera una manera de saber si ya estoy dentro de un bloque de animation, es posible que resuelva mi problema; Simplemente no llamaría layoutIfNeeded en absoluto en ese caso. Traté de resolver eso usando uno de estos methods , pero ninguno funcionó. Probablemente porque este es un bloque de animation oculto / mágico que Apple usa internamente.
  • Intente ver si la vista está en la pantalla : el pensamiento con este es que la vista se reportaría fuera de pantalla porque no se ha llamado a viewDidAppear, por lo que no envolvería el código en un bloque de animation. Sin embargo, cuando intenta averiguar si la vista está en la pantalla mirando la propiedad de su window , informa que está en la pantalla y, por lo tanto, esta solución no funcionaría.

Referencia:

Orden de events:

  1. viewDidLoad
    • textField.becomeFirstResponder = true
    • regístrese para UIKeyboardWillShowNotification
  2. viewWillAppear
  3. UIKeyboardWillShowNotification se activa
    • myConstraint.constant = keyboardHeight de la notificación
    • layoutIfNeeded llamado dentro de un bloque de animation
  4. viewDidAppear

Información de notificación cuando el usuario teclea en un campo de text:

[UIKeyboardFrameBeginUserInfoKey: NSRect: {{0, 736}, {414, 271}},
UIKeyboardCenterEndUserInfoKey: NSPoint: {207, 600.5},
UIKeyboardBoundsUserInfoKey: NSRect: {{0, 0}, {414, 271}},
UIKeyboardFrameEndUserInfoKey: NSRect: {{0, 465}, {414, 271}},
UIKeyboardAnimationDurationUserInfoKey: 0.25,
UIKeyboardCenterBeginUserInfoKey: NSPoint: {207, 871.5},
UIKeyboardAnimationCurveUserInfoKey: 7,
UIKeyboardIsLocalUserInfoKey: 1]

Información de notificación al llamar a textField.becomeFirstResponder en viewDidLoad (es decir, durante un push):

[ UIKeyboardFrameBeginUserInfoKey : NSRect: {{0, 465 }, {414, 271}},
UIKeyboardCenterEndUserInfoKey: NSPoint: {207, 600.5},
UIKeyboardBoundsUserInfoKey: NSRect: {{0, 0}, {414, 271}},
UIKeyboardFrameEndUserInfoKey: NSRect: {{0, 465}, {414, 271}},
UIKeyboardAnimationDurationUserInfoKey : 0.35 ,
UIKeyboardCenterBeginUserInfoKey : NSPoint: {207, 600.5 },
UIKeyboardAnimationCurveUserInfoKey: 7,
UIKeyboardIsLocalUserInfoKey: 1]

(Diferencias en negrita.)