¿Son animables las restricciones de Nslayout? [duplicar]


Esta pregunta ya tiene una respuesta aquí:

Estoy tratando de animar algunas vistas para que sean bloqueadas por el teclado gigante en paisaje. Funciona bien si simplemente animo los marcos, pero otros han sugerido que esto es contraproducente y debería actualizar el NSLayoutConstraints lugar. Sin embargo, no parecen ser animada. ¿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.

Author: borrrden, 2012-10-17

4 answers

Simplemente sigue este patrón exacto:

self.heightFromTop.constant = 550.0f;
[myView setNeedsUpdateConstraints];

[UIView animateWithDuration:0.25f animations:^{
   [myView layoutIfNeeded];
}];

Donde myView es la vista a la que se añadió self.heightFromTop. Su vista está "saltando" porque lo único que hizo en el bloque de animación fue establecer la restricción, lo que no causa diseños inmediatamente. En su código, el diseño ocurre en el siguiente bucle de ejecución después de establecer heightFromTop.constant, y en ese momento ya está fuera del alcance del bloque de animación.

En Swift 2:

self.heightFromTop.constant = 550
myView.setNeedsUpdateConstraints()

UIView.animateWithDuration(0.25, animations: {
   myView.layoutIfNeeded()
})
 458
Author: John Estropia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-03 15:55:53

La forma sugerida por Apple es un poco diferente ( Ver ejemplo en la sección "Animar cambios realizados por Diseño automático" ). Primero debe llamar a layoutIfNeeded antes de la animación. A continuación, agregue su material de animación dentro del bloque de animación y luego llame layoutIfNeeded al final de nuevo. Para los chicos como yo que están haciendo la transición al diseño automático, es una forma más similar a las animaciones anteriores que estábamos haciendo con los marcos dentro de los bloques de animación. Sólo tenemos que llamar a Layout Si es necesario dos veces-antes 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

}];
 77
Author: Centurion,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-23 11:22:58

Probé el enfoque de @Centurion, pero de alguna manera mi vista se animaría a un marco incorrecto si se carga desde el guion gráfico. El problema desaparece si remplazo 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];
}];
 9
Author: Joseph Lin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-24 00:59:05

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:

Diseño Automático Guía https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html (hasta la parte inferior de la página).

Algunas diferencias con la respuesta de erurainon:

  1. Call layoutIfNeeded on the container view before the call to an animation method (and instead of setNeedsUpdateConstraints on MyView).
  2. Establecer la nueva restricción en las animaciones bloque.
  3. Llame a layoutIfNeeded en la vista contenedor en el método animations (después de establecer la restricción), en lugar de en MyView.

Esto se adherirá al patrón sugerido por Apple en el enlace anterior.

Un ejemplo

Quería animar una vista en particular, cerrándola o expandiéndola con el clic de un botón. Dado que estoy usando 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. Es necesario utilizar este método y no viewWillAppear cuando se utiliza autolayout. Dado que viewDidLayoutSubviews puede ser llamado muchas veces, he utilizado un BOOL para hacerme saber acerca de la primera ejecución para 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 fragmento de acción pequeño

// 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.

 4
Author: Scott Carter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-01 19:49:44