¿Son NSLayoutConstraints animatables?

Estoy tratando de animar algunas vistas para que estén bloqueadas por el keyboard gigante en el paisaje. Funciona bien si simplemente animo los frameworks, pero otros han sugerido que esto es contraproducente y debería actualizar las NSLayoutConstraints. Sin embargo, no parecen ser animables. ¿Alguien ha conseguido que funcionen 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 vista está "saltando" porque lo único que hizo en el bloque de animation fue establecer la restricción, que no causa layouts de forma inmediata. En su código, el layout se produce en el siguiente bucle de ejecución después de establecer heightFromTop.constant , y para ese momento ya se encuentra fuera del scope del bloque de animation.

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 ( Vea el ejemplo en la sección "Animación de cambios realizados por layout automático" ). Primero debe llamar a layoutIfNeeded antes de la animation. A continuación, agregue sus elementos de animation dentro del bloque de animation y luego llame a layoutIfNeeded al final nuevamente. Para los chicos como yo que estamos en transición a la reproducción automática, su forma más similar a las animaciones anteriores que estábamos haciendo con los frameworks dentro de los bloques de animation. Solo necesitamos llamar a layoutIfNeeded 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 }]; 

Intenté el enfoque de @ Centurion, pero de alguna manera mi vista se animaría a un marco incorrecto si está cargado desde el guión gráfico. El problema desaparece si reemploop el primer 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 path correcto, pero me gustaría proponer una respuesta un poco 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 layout 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. Llamada layoutIfNeeded en la vista contenedor antes de la llamada a un método de animation (y en lugar de setNeedsUpdateConstraints en myView).
  2. Establezca la nueva restricción en el bloque de animaciones.
  3. Llamada layoutIfNeeded en la vista de contenedor en el método de animaciones (después de establecer la restricción), en lugar de en myView.

Esto se adhirirá 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 button. Como estoy usando la reproducción automática y no quería codificar en forma dura ninguna dimensión (en mi altura de caso) en el código, decidí capturar la altura en viewDidLayoutSubviews. Debe utilizar este método y no visualizarlo cuando utilice Autolayout. Como viewDidLayoutSubviews se puede llamar muchas veces, utilicé un BOOL para informarme sobre la primera ejecución para mi initialization.

 // 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 fragment 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 de pequeño fragment 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.