2010-07-19 12 views
7

He estado tratando de entender qué está mal con mi animación y todavía no lo he resuelto. Creo que debería ser muy sencillo, pero probablemente haya algo que me falta, incluso después de leer muchos ejemplos y documentación.CABasicAnimation no anima mi propiedad

Mi problema proviene originalmente del hecho de que en el iPhone, no puede cambiar el tamaño de las capas automáticamente (con la vista). La documentación dice lo contrario, pero no hay autoresizeMask para la capa en los SDK. Así que decidí hacer una pequeña solución y animar la capa yo mismo.

Tengo este simple código que debería hacer una animación de cambio de tamaño simple. Los valores son buenos e incluso configuro el delegado para rastrear si el inicio/fin del anim.

// I've got a property named layerImage (which is a CALayer) 
- (void)animateTestWithFrame:(CGRect)value { 
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"layerImage.frame"]; 
    animation.duration = 1; 
    animation.fromValue = [NSValue valueWithCGRect:self.frame]; 
    animation.toValue = [NSValue valueWithCGRect:value]; 
    animation.removedOnCompletion = YES; 
    animation.delegate = self; 

    [self.layer addAnimation:animation forKey:@"layerImage.frame"]; 
} 

¿Alguna idea? (Este punto de vista que contiene el código es la subvista de una vista secundaria de la ventana si eso podría hacer una diferencia)

--- --- EDITAR

Parece que el marco no está animatable través CABasicAnimation y el llamado propiedad "marco". Cuando uso límites, tengo un resultado extraño, pero al menos obtengo algo. Seguiremos investigando sobre esto.

Respuesta

26

Así que es bueno que hayas averiguado las cosas aquí, pero la respuesta a tu pregunta tiene algunas inexactitudes. Permítanme corregir algunas cosas:

La propiedad de marco puede estar animada, pero no con animación explícita. Si hace lo siguiente a una capa distinta de la capa raíz de un punto de vista, el marco se animará bien:

[CATransaction begin]; 
[CATransaction setAnimationDuration:2.0f]; 
[animationLayer setFrame:CGRectMake(100.0f, 100.0f, 100.0f, 100.0f)]; 
[CATransaction commit]; 

Recuerde que el establecimiento de una propiedad en una capa se animar a que el cambio de propiedad por defecto. De hecho, debe desactivar las animaciones en un cambio de propiedad si no desea que se anime. (Por supuesto, esto solo es cierto si está animando una capa que no sea la capa raíz de una vista). Tiene razón al animar tanto la posición como los límites si necesita usar una animación explícita.

Usted puede animar el marco en una UIView usando animación implícita:

[UIView beginAnimations:nil context:NULL]; 
[UIView setAnimationDuration:3.0f]; 
[[self view] setFrame:CGRectMake(45.0f, 45.0f, 100.0f, 100.0f)]; 
[UIView commitAnimations]; 

Esto animará de trama actual de la vista (límites y posición) a x = 45,0, y = 45,0, w = 100,0 , h = 100.0.

Parece que también puede estar malinterpretando la diferencia entre una animación y una capa. Agrega animaciones a las capas, pero agregar una animación a una capa no establece automáticamente la propiedad que está animando.

CALayers son objetos modelo. Contienen información sobre la capa que eventualmente se procesa en la pantalla. Usted debe establecer la propiedad de una capa si desea que esa propiedad tenga realmente ese valor. Si simplemente anima la propiedad, solo será una representación visual y no real, lo que significa que el valor vuelve al valor original de la capa porque nunca la cambió realmente.

Lo que me lleva al siguiente punto. Usted dijo:

Uso "animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards;" para asegurar que los valores no se resetean al final de la animación

Esto no es exactamente correcto. Estos dos valores simplemente hacen que la animación permanezca en su posición final visualmente, sin embargo, los valores reales de la capa no han cambiado. Siguen siendo exactamente los mismos valores que cuando comenzaste la animación. En general (con algunas excepciones) no desea utilizar estos dos parámetros porque son visual solamente. Lo que desea es establecer realmente el valor de capa para la propiedad que está animando.

Digamos, por ejemplo, que desea animar la posición de su capa mediante una animación explícita. Aquí está el código que desea:

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; 
[animation setFromValue:[NSValue valueWithCGPoint:CGPointMake(70.0f, 70.0f)]]; 
[animation setToValue:[NSValue valueWithCGPoint:CGPointMake(150.0f, 150.0f)]]; 
[animation setDuration:2.0f]; 

// Actually set the position on the *layer* that you want it to be 
// when the animation finishes. 
[animationLayer setPosition:CGPointMake(150.0f, 150.0f)]; 

// Add the animation for the position key to ensure that you 
// override the animation for position changes with your animation. 
[animationLayer addAnimation:animation forKey:@"position"]; 

También le recomendamos considerar agrupar animaciones. Con un grupo de animación, puede agrupar varias animaciones y luego controlar cómo se relacionan entre sí.En su caso, la duración de sus límites y las animaciones de posición son las mismas, por lo que lo que está intentando hacer funcionará bien sin un grupo, pero si desea compensar el inicio de las animaciones, por ejemplo, no desea el puesto. Para comenzar la animación hasta uno o dos segundos en la animación de cuadros, puede escalonarlos estableciendo el valor beginTime de la animación de posición.

Finalmente, me gustaría saber por qué no puede usar las animaciones implícitas disponibles en UIView. Los uso en la gran mayoría del código de Core Animation que escribo y no puedo ver por qué esto no funcionaría para su situación.

Saludos cordiales.

+0

Gracias por la explicación Matt. Pero para el problema de animación de UIView, estaba relacionado solo con mi capa. Por lo general, no tengo ningún problema con la animación con UIView, pero en mi caso causaba comportamientos extraños. Cambiar el tamaño del marco de vista no cambia el tamaño de la capa en el iPhone (ya que no hay un mecanismo automático en iPhone, solo para vistas y subvistas). Entonces, tienes que hacerlo "manualmente". – Sauleil

+0

¡Buena respuesta! Muy detallado. Te habría dado 2 puntos en caso de que pudiera. – Soberman

3

La ruta de la clave solo debe ser la ruta de la clave de la propiedad, no el nombre del objeto también.

Utilice esta

[CABasicAnimation animationWithKeyPath:@"frame"] 

en lugar de esta

[CABasicAnimation animationWithKeyPath:@"layerImage.frame"] 

Y justo Por cierto, cuando se agrega la animación a una capa, los doen't clave significan la propiedad clave para animar. Solo la clave (nombre) que desea que tenga esta animación (esto se refiere a la última línea de su código)

+0

está bien para el marco, pero ¿cómo sabe CABasicAnimation qué capa para animar esa propiedad? ¿Eso significa que tengo que hacer [layerImage addAnimation: animación ...]. (Lo intenté y tampoco funciona) – Sauleil

+0

Si estás haciendo animación en un objeto diferente de ti (en ese método), simplemente usa [auto.layerImage.layer addAnimation: animation ...] – tadejsv

+0

Una cosa más: I no entiendo por qué está utilizando la CABasicAnimation aquí. Las animaciones de UIView servirían. – tadejsv

1

Entonces, la solución era animar los @ "límites" y la @ "posición" de la capa porque el marco no es animable en iPhone. Me llevó algo de tiempo entender que la posición era el centro de la capa y que el tamaño de los límites se extendía desde el centro, pero esa era la parte fácil.

Por lo tanto, lo que hice en hoja de vida era:

  1. En el setFrame, cree 2 animaciones con los límites y propiedad de posición.
  2. Use "animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards;" para asegurarse de que los valores no se restablecen al final de la animación
  3. Registre el delegado en self para implementar "animationDidStop: finished:". Parece que todavía necesita establecer los valores: "layerImage.bounds = [animation.toValue CGRectValue]; layerImage.position = [animation.toValue CGPointValue];".

No pude usar el sistema de animación UIView directamente porque no estaba haciendo lo que quería en las capas.

Gracias tadej5553 por señalarme el problema de capa que tuve con el "addAnimation". Así que aquí está el código para aquellos que quieran ver cómo se ve.

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { 
    CABasicAnimation *animation = (CABasicAnimation*)anim; 

    if ([animation.keyPath isEqualToString:@"bounds"]) { 
     layerImage.bounds = [animation.toValue CGRectValue]; 
    } else if ([animation.keyPath isEqualToString:@"position"]) { 
     layerImage.position = [animation.toValue CGPointValue]; 
    } 
} 

- (void)setFrame:(CGRect)value { 
    CGRect bounds = CGRectMake(0, 0, value.size.width, value.size.height); 

    if ([UIView isAnimationStarted]) { 
     // animate the bounds 
     CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds"]; 
     animation.duration = [UIView animationDuration]; 
     animation.fromValue = [NSValue valueWithCGRect:layerImage.bounds]; 
     animation.toValue = [NSValue valueWithCGRect:bounds]; 
     animation.removedOnCompletion = NO; 
     animation.fillMode = kCAFillModeForwards; 
     animation.timingFunction = [UIView animationFunction]; 
     animation.delegate = self; 
     [layerImage addAnimation:animation forKey:@"BoundsAnimation"]; 

     // animate the position so it stays at 0, 0 of the frame. 
     animation = [CABasicAnimation animationWithKeyPath:@"position"]; 
     animation.duration = [UIView animationDuration]; 
     animation.fromValue = [NSValue valueWithCGPoint:layerImage.position]; 
     animation.toValue = [NSValue valueWithCGPoint:CGPointMake(bounds.size.width/2, bounds.size.height/2)]; 
     animation.removedOnCompletion = NO; 
     animation.fillMode = kCAFillModeForwards; 
     animation.timingFunction = [UIView animationFunction]; 
     animation.delegate = self; 
     [layerImage addAnimation:animation forKey:@"PositionAnimation"]; 
    } else { 
     layerImage.frame = bounds; 
    } 

    [super setFrame:value]; 
} 
Cuestiones relacionadas