Animación UIScrollView de altura y contenido El contenido "salta" contenido desde la parte inferior

Tratando de hacer algo similar al comportamiento de Messages.app, tengo un UIScrollView y debajo de él un campo de text e intentando animarlo para que cuando el keyboard aparezca todo se mueve arriba del keyboard usando una restricción que mueve el campo hacia arriba ( y la altura de UIScrollView cambia también debido a la reproducción automática) y también configurando el contentOffset la contentOffset de desplazamiento para desplazarse hacia abajo al mismo time.

El código logra el resultado final deseado, pero durante la animation justo cuando comienza la animation del keyboard, la vista de desplazamiento se vuelve en blanco y luego el contenido se desplaza desde la parte inferior, en lugar de desplazarse desde la position en que se encontraba cuando comenzó la animation.

La animation es esta:

 - (void)updateKeyboardConstraint:(CGFloat)height animationDuration:(NSTimeInterval)duration { self.keyboardHeight.constant = -height; [self.view setNeedsUpdateConstraints]; [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{ [self.view layoutIfNeeded]; self.collectionView.contentOffset = CGPointMake(0, self.collectionView.contentSize.height - self.collectionView.bounds.size.height); } completion:nil]; } 

Un video del problema está disponible aquí .

¡Gracias!

Puede ser un error en UIKit. Ocurre cuando hay un cambio simultáneo en el size y el contentOffset del contentOffset de UIScrollView . Sería interesante comprobar si este comportamiento también ocurre sin el layout automático.

Encontré dos soluciones a este problema.

Usando ContentInset (el enfoque de Mensajes)

Como se puede ver en la aplicación Mensajes, la altura de UIScrollView no cambia cuando se muestra un keyboard: los posts se ven debajo del keyboard. Puedes hacerlo de la misma manera. Eliminar la restricción entre UICollectionView y la vista que contiene UITextField y UIButton (lo llamaré messageComposeView ). A continuación, agregue restricción entre UICollectionView y la Bottom Layout Guide . Mantenga la restricción entre messageComposeView y la Bottom Layout Guide . Luego use contentInset para mantener visualmente el último elemento de UICollectionView sobre el keyboard. Lo hice de la siguiente manera:

 - (void)updateKeyboardConstraint:(CGFloat)height animationDuration:(NSTimeInterval)duration { self.bottomSpaceConstraint.constant = height; [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{ CGPoint bottomOffset = CGPointMake(0, self.collectionView.contentSize.height - (self.collectionView.bounds.size.height - height)); [self.collectionView setContentOffset:bottomOffset animated:YES]; [self.collectionView setContentInset:UIEdgeInsetsMake(0, 0, height, 0)]; [self.view layoutIfNeeded]; } completion:nil]; } 

Aquí self.bottomSpaceConstraint es una restricción entre messageComposeView y la Bottom Layout Guide . Aquí está el video que muestra cómo funciona. ACTUALIZACIÓN 1: Aquí está la fuente de mi proyecto en GitHub . Este proyecto es un poco simplificado. Debería haber tenido en count las opciones aprobadas en la notificación en - (void)keyboardWillShow:(NSNotification *)notif .

Realizar cambios en una queue

No es una solución exacta, pero el desplazamiento funciona bien si lo mueve al bloque de finalización:

 } completion:^(BOOL finished) { [self.collectionView setContentOffset:CGPointMake(0, self.collectionView.contentSize.height - self.collectionView.bounds.size.height) animated:YES]; }]; 

Tarda 0,25 segundos para que el keyboard se muestre, por lo que la diferencia entre los comienzos de las animaciones puede ser perceptible. Las animaciones también se pueden hacer en el order invertido.

ACTUALIZACIÓN 2: También noté que el código de OP funciona bien con este cambio:

 CGPoint bottomOffset = CGPointMake(0, self.collectionView.contentSize.height - (self.collectionView.bounds.size.height - height)); 

pero solo cuando la height contentSize es menor que un valor fijo ( en mi caso alnetworkingedor de 800 , pero mi layout puede ser un poco diferente).

Al final, creo que el enfoque que presenté en Using contentInset (the Messages approach) es mejor que cambiar el tamaño de UICollectionView . Al usar contentInset , también obtenemos la visibilidad de los elementos debajo del keyboard. Ciertamente se adapta mejor al estilo iOS 7.

Tuve un problema similar: al animar el marco y compensar el contenido, saltaba justo antes de animar a su position, y la solución ENTERA solo estaba agregando UIViewAnimationOptionBeginFromCurrentState a las opciones de animation. Voila!

¿Realmente se necesita setNeedsUpdateConstraints ? ¿El layout automático no lo hace automáticamente?

Si no, te sugiero que realices toda la animation de cambio de tamaño solo sin usar el layout automático. parece que el problema es cuando intentas hacer ambas animaciones juntas (pero no está al mismo time)

Intente eliminar la línea [self.view layoutIfNeeded] y vea si el problema persiste o si aparecen otros problemas y, si es así, si están relacionados de alguna manera.

Además, siempre es una buena idea restablecer la position de sus vistas justo antes de la animation. Así que trate de establecer el desplazamiento normal justo antes de la línea de animation (y tal vez incluso llame al método layoutIfNeeded allí), como orderar todo antes de comenzar la animation.

No estoy seguro de que sea exactamente el comportamiento que desea, pero tal vez pueda darle un empujón en la dirección correcta: proyecto Github

Lo que hice fue configurar dos restricciones, una para el campo de text (en la guía inferior) y la otra para la vista de desplazamiento (para el campo de text).

Luego, cuando llamo a la animation, animo la propiedad "central" de ambos elementos, no el contenido Desconfianza, y trato el valor de la animation y el valor de la restricción por separado.

El resultado final está aquí:

video de Youtube

Tuve el mismo problema, pude resolverlo al registrar NSNotifications para keyboard / ocultar y mostrar. Estoy proporcionando el código para el mismo. Espero que te ayude.

simplemente declare BOOL isMovedUp en su class .h

En ViewDidLoad

 // registering notifications for keyboard/hiding and showing [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide) name:UIKeyboardWillHideNotification object:nil]; } #pragma mark-keyboard notifications - (void)keyboardWillShow { // Animate the current view out of the way if (isMovedUp==YES){ } else { [self setViewMovedUp:YES]; isMovedUp=YES; } } - (void)keyboardWillHide { if (isMovedUp==YES) { [self setViewMovedUp:NO]; isMovedUp=NO; } } //method for view transformation -(void)setViewMovedUp:(BOOL)movedUp { [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.5]; // if you want to slide up the view CGRect rect = self.winePopUpView.frame; if (movedUp) { //isKeyBoardDown = NO; // 1. move the view's origin up so that the text field that // will be hidden come above the keyboard // 2. increase the size of the view so that the area // behind the keyboard is covenetworking up. rect.origin.y -= 100; //rect.size.height += 100; } else { // revert back to the normal state. rect.origin.y += 100; //rect.size.height -= 100; //isKeyBoardDown = YES; } self.winePopUpView.frame = rect; [UIView commitAnimations]; }