¿Son NSLayoutConstraints animables?

Intento animar algunas vistas para que sean bloqueadas por el teclado gigante en el paisaje. Funciona bien si simplemente animo los fotogtwigs, pero otros han sugerido que esto es contraproducente y en su lugar debería actualizar NSLayoutConstraints. Sin embargo, no parecen ser animables. ¿Alguien ha conseguido que trabajen con éxito?

//heightFromTop is an NSLayoutConstraint referenced from IB [UIView animateWithDuration:0.25 animations:^{ self.heightFromTop.constant= 550.f; }]; 

El resultado es un salto instantáneo a la altura en cuestión.

Solo sigue este patrón exacto:

 self.heightFromTop.constant = 550.0f; [myView setNeedsUpdateConstraints]; [UIView animateWithDuration:0.25f animations:^{ [myView layoutIfNeeded]; }]; 

donde myView es la vista donde se agregó self.heightFromTop . Su punto de vista es “saltar” porque lo único que hizo en el bloque de animación fue establecer la restricción, que no causa diseños de forma inmediata. En su código, el diseño ocurre en el siguiente ciclo de ejecución después de establecer heightFromTop.constant , y para ese momento ya se encuentra fuera del scope del bloque de animación.

En Swift 2:

 self.heightFromTop.constant = 550 myView.setNeedsUpdateConstraints() UIView.animateWithDuration(0.25, animations: { myView.layoutIfNeeded() }) 

La forma sugerida por Apple es un poco diferente ( ver ejemplo en la sección “Animación de cambios realizados por diseño automático” ). Primero debe llamar al layout IfNeeded antes de la animación. A continuación, agregue sus elementos de animación dentro del bloque de animación y luego llame de nuevo al layout IfNeeded al final. Para los tipos como yo que están pasando a la fase de autodiseño, es más similar a las animaciones anteriores que estábamos haciendo con los cuadros dentro de los bloques de animación. Solo debemos llamar al layout IfNeeded dos veces antes de las animaciones y después de las animaciones:

 [self.view layoutIfNeeded]; // Ensures that all pending layout operations have been completed [UIView animateWithDuration:1.0f animations:^{ // Make all constraint changes here self.heightFromTop.constant= 550.f; [self.view layoutIfNeeded]; // Forces the layout of the subtree animation block and then captures all of the frame changes }]; 

Probé el enfoque de @ Centurion, pero de alguna manera mi vista se animaría a un fotogtwig incorrecto si se carga desde el guión gráfico. El problema desaparece si reemplazo el primer layoutIfNeeded si lo layoutIfNeeded con updateConstraintsIfNeeded , aunque no tengo idea de por qué. Si alguien puede dar una explicación, sería muy apreciado.

 [self.view updateConstraintsIfNeeded]; [UIView animateWithDuration:1.0 animations:^{ self.myConstraint.constant= 100; [self.view layoutIfNeeded]; }]; 

Estaba teniendo un problema similar y este hilo fue de gran ayuda para superarlo.

La respuesta de erurainon me puso en el camino correcto, pero me gustaría proponer una respuesta ligeramente diferente. El código sugerido de erurainon no funcionó para mí, ya que todavía tenía un salto en lugar de una transición animada. El enlace proporcionado por cnotethegr8 me dio la respuesta de trabajo:

Guía de diseño automático https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html (hasta el final de la página).

Algunas diferencias de la respuesta de erurainon:

  1. Call layout IfNeeded en la vista contenedor antes de la llamada a un método de animación (y en lugar de setNeedsUpdateConstraints en myView).
  2. Establezca la nueva restricción en el bloque de animaciones.
  3. El layout de llamada se necesita en la vista de contenedor en el método de animaciones (después de establecer la restricción), en lugar de hacerlo en myView.

Esto se apegará al patrón sugerido por Apple en el enlace de arriba.

Un ejemplo

Quería animar una vista en particular, cerrarla o expandirla con solo hacer clic en un botón. Como utilizo el diseño automático y no quería codificar ninguna dimensión (en mi caso, altura) en el código, decidí capturar la altura en viewDidLayoutSubviews. Necesita usar este método y no ver WillAppear cuando usa el diseño automático. Como viewDidLayoutSubviews se puede llamar muchas veces, utilicé un BOOL para informarme sobre la primera ejecución de mi inicialización.

 // Code snippets @property (weak, nonatomic) IBOutlet UIView *topView; // Container for minimalView @property (weak, nonatomic) IBOutlet UIView *minimalView; // View to animate @property (nonatomic) CGFloat minimalViewFullHeight; // Original height of minimalView @property (weak, nonatomic) IBOutlet NSLayoutConstraint *minimalViewHeightConstraint; @property (nonatomic) BOOL executedViewDidLayoutSubviews; - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; // First execution of viewDidLayoutSubviews? if(!self.executedViewDidLayoutSubviews){ self.executedViewDidLayoutSubviews = YES; // Record some original dimensions self.minimalViewFullHeight = self.minimalView.bounds.size.height; // Setup our initial view configuration & let system know that // constraints need to be updated. self.minimalViewHeightConstraint.constant = 0.0; [self.minimalView setNeedsUpdateConstraints]; [self.topView layoutIfNeeded]; } } 

Cambiar el tamaño del fragmento de acción completo

 // An action to close our minimal view and show our normal (full) view - (IBAction)resizeFullAction:(UIButton *)sender { [self.topView layoutIfNeeded]; [UIView transitionWithView:self.minimalView duration:1.0 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{ self.minimalViewHeightConstraint.constant = 0.0; // Following call to setNeedsUpdateConstraints may not be necessary [self.minimalView setNeedsUpdateConstraints]; [self.topView layoutIfNeeded]; } completion:^(BOOL finished) { ; }]; // Other code to show full view // ... } 

Cambiar el tamaño del pequeño fragmento de acción

 // An action to open our minimal view and hide our normal (full) view - (IBAction)resizeSmallAction:(UIButton *)sender { [self.topView layoutIfNeeded]; [UIView transitionWithView:self.minimalView duration:1.0 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{ self.minimalViewHeightConstraint.constant = self.minimalViewFullHeight; [self.minimalView setNeedsUpdateConstraints]; [self.topView layoutIfNeeded]; } completion:^(BOOL finished) { ; }]; // Other code to hide full view // ... } 

Puede usar animateWithDuration en lugar de transitionWithView si lo desea.

Espero que esto ayude.