¿Cómo animo los cambios de restricciones?

Resuelto DBD asked hace 54 años • 12 respuestas

Estoy actualizando una aplicación antigua con un anuncio AdBannerViewy cuando no hay ningún anuncio, se desliza fuera de la pantalla. Cuando hay un anuncio se desliza en la pantalla. Cosas básicas.

Al estilo antiguo, configuro el marco en un bloque de animación. Nuevo estilo, tengo una IBOutletrestricción de diseño automático que determina la Yposición, en este caso es la distancia desde la parte inferior de la supervista, y modifico la constante:

- (void)moveBannerOffScreen {
    [UIView animateWithDuration:5 animations:^{
        _addBannerDistanceFromBottomConstraint.constant = -32;
    }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen {
    [UIView animateWithDuration:5 animations:^{
        _addBannerDistanceFromBottomConstraint.constant = 0;
    }];
    bannerIsVisible = TRUE;
}

Y el banner se mueve exactamente como se esperaba, pero sin animación.


ACTUALIZACIÓN: Volví a ver la charla de la WWDC 12 sobre Mejores prácticas para dominar el diseño automático , que cubre la animación. Se analiza cómo actualizar restricciones usando CoreAnimation :

Lo intenté con el siguiente código, pero obtuve exactamente los mismos resultados:

- (void)moveBannerOffScreen {
    _addBannerDistanceFromBottomConstraint.constant = -32;
    [UIView animateWithDuration:2 animations:^{
        [self.view setNeedsLayout];
    }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen {
    _addBannerDistanceFromBottomConstraint.constant = 0;
    [UIView animateWithDuration:2 animations:^{
        [self.view setNeedsLayout];
    }];
    bannerIsVisible = TRUE;
}

Como nota al margen, lo he comprobado numerosas veces y esto se está ejecutando en el hilo principal .

DBD avatar Jan 01 '70 08:01 DBD
Aceptado

Dos notas importantes:

  1. Debes llamar layoutIfNeededdentro del bloque de animación. De hecho, Apple recomienda llamarlo una vez antes del bloque de animación para asegurarse de que se hayan completado todas las operaciones de diseño pendientes.

  2. Debe llamarlo específicamente en la vista principal (por ejemplo self.view), no en la vista secundaria que tiene las restricciones adjuntas. Al hacerlo, se actualizarán todas las vistas restringidas, incluida la animación de otras vistas que podrían estar restringidas a la vista cuya restricción cambió (por ejemplo, la Vista B está adjunta a la parte inferior de la Vista A y usted acaba de cambiar el desplazamiento superior de la Vista A y desea que la Vista B animar con él)

Prueba esto:

C objetivo

- (void)moveBannerOffScreen {
    [self.view layoutIfNeeded];

    [UIView animateWithDuration:5
        animations:^{
            self._addBannerDistanceFromBottomConstraint.constant = -32;
            [self.view layoutIfNeeded]; // Called on parent view
        }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen { 
    [self.view layoutIfNeeded];

    [UIView animateWithDuration:5
        animations:^{
            self._addBannerDistanceFromBottomConstraint.constant = 0;
            [self.view layoutIfNeeded]; // Called on parent view
        }];
    bannerIsVisible = TRUE;
}

veloz 3

UIView.animate(withDuration: 5) {
    self._addBannerDistanceFromBottomConstraint.constant = 0
    self.view.layoutIfNeeded()
}
g3rv4 avatar Sep 30 '2012 19:09 g3rv4

Agradezco la respuesta proporcionada, pero creo que sería bueno ir un poco más allá.

La animación de bloque básica de la documentación.

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

pero en realidad este es un escenario muy simplista. ¿Qué pasa si quiero animar restricciones de subvista mediante el updateConstraintsmétodo?

Un bloque de animación que llama al método updateConstraints de subvistas.

[self.view layoutIfNeeded];
[self.subView setNeedsUpdateConstraints];
[self.subView updateConstraintsIfNeeded];
[UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionLayoutSubviews animations:^{
    [self.view layoutIfNeeded];
} completion:nil];

El método updateConstraints se anula en la subclase UIView y debe llamar a super al final del método.

- (void)updateConstraints
{
    // Update some constraints

    [super updateConstraints];
}

La Guía de AutoLayout deja mucho que desear pero vale la pena leerla. Yo mismo estoy usando esto como parte de una UISwitchque alterna una subvista con un par de UITextFields con una animación de colapso simple y sutil (0,2 segundos de duración). Las restricciones para la subvista se manejan en los métodos updateConstraints de las subclases de UIView como se describe anteriormente.

Cameron Lowell Palmer avatar Jan 22 '2014 00:01 Cameron Lowell Palmer