CABasicAnimation se restablece al valor inicial una vez completada la animación


Estoy rotando un CALayer y tratando de detenerlo en su posición final después de que se complete la animación.

Pero una vez completada la animación se restablece a su posición inicial.

(los documentos de xcode dicen explícitamente que la animación no actualizará el valor de la propiedad.)

Cualquier sugerencia de cómo lograr esto.

Author: Nilesh Ukey, 2011-05-19

13 answers

Edit : Aquí está la respuesta, es una combinación de mi respuesta y la de Krishnan.

cabasicanimation.fillMode = kCAFillModeForwards;
cabasicanimation.removedOnCompletion = NO;

El valor predeterminado es kCAFillModeRemoved. (Que es el comportamiento de reinicio que estás viendo.)

 235
Author: Nilesh Ukey,
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-02-25 14:45:32

El problema con removedOnCompletion es que el elemento UI no permite la interacción del usuario.

La técnica I consiste en establecer el valor FROM en la animación y el valor TO en el objeto. La animación llenará automáticamente el valor A antes de que comience, y cuando se elimine dejará el objeto en su estado correcto.

// fade in
CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath: @"opacity"];
alphaAnimation.fillMode = kCAFillModeForwards;

alphaAnimation.fromValue = NUM_FLOAT(0);
self.view.layer.opacity = 1;

[self.view.layer addAnimation: alphaAnimation forKey: @"fade"];
 70
Author: Leslie Godwin,
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
2012-12-13 06:04:23

Establezca la siguiente propiedad:

animationObject.removedOnCompletion = NO;
 16
Author: Krishnan,
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
2011-05-19 13:07:37

Simplemente póngalo dentro de su código

CAAnimationGroup *theGroup = [CAAnimationGroup animation];

theGroup.fillMode = kCAFillModeForwards;

theGroup.removedOnCompletion = NO;
 11
Author: msk_sureshkumar,
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
2012-02-27 06:38:21

Simplemente puede establecer la clave de CABasicAnimation a position cuando la agregue a la capa. Al hacer esto, anulará la animación implícita realizada en la posición para el pase actual en el bucle de ejecución.

CGFloat yOffset = 30;
CGPoint endPosition = CGPointMake(someLayer.position.x,someLayer.position.y + yOffset);

someLayer.position = endPosition; // Implicit animation for position

CABasicAnimation * animation =[CABasicAnimation animationWithKeyPath:@"position.y"]; 

animation.fromValue = @(someLayer.position.y);
animation.toValue = @(someLayer.position.y + yOffset);

[someLayer addAnimation:animation forKey:@"position"]; // The explicit animation 'animation' override implicit animation

Puede tener más información sobre la sesión de video de Apple WWDC 2011 421-Core Animation Essentials (en medio del video)

 9
Author: yageek,
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
2016-01-05 06:28:56

Simplemente configurar fillMode y removedOnCompletion no funcionó para mí. Resolví el problema estableciendo todas de las siguientes propiedades en el objeto CABasicAnimation:

CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:@"transform"];
ba.duration = 0.38f;
ba.fillMode = kCAFillModeForwards;
ba.removedOnCompletion = NO;
ba.autoreverses = NO;
ba.repeatCount = 0;
ba.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.85f, 0.85f, 1.0f)];
[myView.layer addAnimation:ba forKey:nil];

Este código transforma myView al 85% de su tamaño (3a dimensión inalterada).

 5
Author: Paula Vasconcelos Gueiros,
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
2016-02-12 20:11:14

@Leslie Godwin la respuesta no es realmente buena, "yo.vista.estrato.opacity = 1; " se hace inmediatamente (tarda alrededor de un segundo), por favor arregle alphaAnimation.duración hasta 10.0, si tienes dudas. Tienes que quitar esta línea.

Por lo tanto, cuando se fija fillMode a kCAFillModeForwards y removedOnCompletion a NO, se deja que la animación permanezca en la capa. Si arreglas el delegado de animación e intentas algo como:

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
 [theLayer removeAllAnimations];
}

...la capa se restaura inmediatamente en el momento de ejecutar esta línea. Es lo que queríamos evitar.

Debe corregir la propiedad layer antes de eliminar la animación de la misma. Prueba esto:

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
     if([anim isKindOfClass:[CABasicAnimation class] ]) // check, because of the cast
    {
        CALayer *theLayer = 0;
        if(anim==[_b1 animationForKey:@"opacity"])
            theLayer = _b1; // I have two layers
        else
        if(anim==[_b2 animationForKey:@"opacity"])
            theLayer = _b2;

        if(theLayer)
        {
            CGFloat toValue = [((CABasicAnimation*)anim).toValue floatValue];
            [theLayer setOpacity:toValue];

            [theLayer removeAllAnimations];
        }
    }
}
 4
Author: tontonCD,
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-09-19 15:41:41

Un CALayer tiene una capa de modelo y una capa de presentación. Durante una animación, la capa de presentación se actualiza independientemente del modelo. Cuando se completa la animación, la capa de presentación se actualiza con el valor del modelo. Si desea evitar un salto discordante después de que termine la animación, la clave es mantener las dos capas sincronizadas.

Si conoce el valor final, puede configurar el modelo directamente.

self.view.layer.opacity = 1;

Pero si tienes una animación donde no sabes el final posición (por ejemplo, un fundido lento que el usuario puede pausar y luego revertir), luego puede consultar la capa de presentación directamente para encontrar el valor actual y luego actualizar el modelo.

NSNumber *opacity = [self.layer.presentationLayer valueForKeyPath:@"opacity"];
[self.layer setValue:opacity forKeyPath:@"opacity"];

Extraer el valor de la capa de presentación también es particularmente útil para escalar o girar las rutas de teclado. (e. g.transform.scale, transform.rotation)

 4
Author: Jason Moore,
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
2015-12-22 15:03:00

Sin usar el removedOnCompletion

Puedes probar esta técnica:

self.animateOnX(item: shapeLayer)

func animateOnX(item:CAShapeLayer)
{
    let endPostion = CGPoint(x: 200, y: 0)
    let pathAnimation = CABasicAnimation(keyPath: "position")
    //
    pathAnimation.duration = 20
    pathAnimation.fromValue = CGPoint(x: 0, y: 0)//comment this line and notice the difference
    pathAnimation.toValue =  endPostion
    pathAnimation.fillMode = kCAFillModeBoth

    item.position = endPostion//prevent the CABasicAnimation from resetting item's position when the animation finishes

    item.add(pathAnimation, forKey: nil)
}
 3
Author: Ayman Ibrahim,
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-07-30 10:31:21

Esto funciona:

let animation = CABasicAnimation(keyPath: "opacity")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 0.3

someLayer.opacity = 1 // important, this is the state you want visible after the animation finishes.
someLayer.addAnimation(animation, forKey: "myAnimation")

Core animation muestra una 'capa de presentación' encima de su capa normal durante la animación. Por lo tanto, establezca la opacidad (o lo que sea) a lo que desea ver cuando la animación termine y la capa de presentación desaparezca. Haga esto en la línea antes de agregar la animación para evitar un parpadeo cuando se complete.

Si desea tener un retraso, haga lo siguiente:

let animation = CABasicAnimation(keyPath: "opacity")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 0.3
animation.beginTime = someLayer.convertTime(CACurrentMediaTime(), fromLayer: nil) + 1
animation.fillMode = kCAFillModeBackwards // So the opacity is 0 while the animation waits to start.

someLayer.opacity = 1 // <- important, this is the state you want visible after the animation finishes.
someLayer.addAnimation(animation, forKey: "myAnimation")

Finalmente, si usas 'removedOnCompletion = false' se filtrará CAAnimations hasta que la capa es finalmente dispuesto-evitar.

 2
Author: Chris,
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
2016-02-10 03:11:45

Así que mi problema era que estaba tratando de rotar un objeto en el gesto de panorámica y por lo que tenía múltiples animaciones idénticas en cada movimiento. Tenía ambos fillMode = kCAFillModeForwards y isRemovedOnCompletion = false pero no ayudó. En mi caso, Tuve que asegurarme de que la clave de animación es diferente cada vez que agrego una nueva animación :

let angle = // here is my computed angle
let rotate = CABasicAnimation(keyPath: "transform.rotation.z")
rotate.toValue = angle
rotate.duration = 0.1
rotate.isRemovedOnCompletion = false
rotate.fillMode = kCAFillModeForwards

head.layer.add(rotate, forKey: "rotate\(angle)")
 2
Author: sunshinejr,
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
2018-05-08 11:21:02

Parece que removedOnCompletion flag establecido en false y fillMode establecido en kCAFillModeForwards tampoco funciona para mí.

Después de aplicar una nueva animación en una capa, un objeto de animación se restablece a su estado inicial y luego se anima desde ese estado. Lo que se debe hacer adicionalmente es establecer la propiedad deseada de la capa de modelo de acuerdo con la propiedad de la capa de presentación antes de establecer una nueva animación como esta:

someLayer.path = ((CAShapeLayer *)[someLayer presentationLayer]).path;
[someLayer addAnimation:someAnimation forKey:@"someAnimation"];
 1
Author: Bartosz Olszanowski,
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
2016-06-07 13:23:41

Core animation mantiene dos jerarquías de capas: la capa de modeloy la capa de presentación. Cuando la animación está en progreso, la capa del modelo está realmente intacta y mantiene su valor inicial. De forma predeterminada, la animación se elimina una vez que se completa. A continuación, la capa de presentación vuelve al valor de la capa de modelo.

Simplemente establecer removedOnCompletion a NO significa que la animación no se eliminará y desperdiciará memoria. Además, la capa modelo y la presentación la capa ya no será sincrónica, lo que puede conducir a errores potenciales.

Así que sería una mejor solución actualizar la propiedad directamente en la capa del modelo al valor final.

self.view.layer.opacity = 1;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.fromValue = 0;
animation.toValue = 1;
[self.view.layer addAnimation:animation forKey:nil];

Si hay alguna animación implícita causada por la primera línea del código anterior, intente desactivar:

[CATransaction begin];
[CATransaction setDisableActions:YES];

self.view.layer.opacity = 1;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.fromValue = 0;
animation.toValue = 1;
[self.view.layer addAnimation:animation forKey:nil];

[CATransaction commit];
 1
Author: Lizhen Hu,
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
2018-06-04 01:15:41