¿Hay algún problema con este uso de kCAMediaTimingFunctionEaseIn?

Estoy usando CALayer , CAReplicatorLayer y CABasicAnimation para animar un gráfico de barras en su lugar. El gráfico de barras está formado por "segmentos" que hago como pequeños rectangularjs con un CALayer . Luego los agrego como subcapas a CAReplicatorLayer en un ciclo. Finalmente, agrego una animation para cada pequeño rectángulo que deja caer el rectángulo en su lugar en el gráfico.

Aquí está el estado final de la animation:

Corregir el estado final de la animación

Y aquí es cómo se ve en medio de la animation:

Estado animador

Aquí está el código que lo hace posible:

 #define BLOCK_HEIGHT 6.0f #define BLOCK_WIDTH 8.0f @interface TCViewController () @property (nonatomic, strong) NSMutableArray * blockLayers; // blocks that fall from the sky @property (nonatomic, strong) NSMutableArray * replicatorLayers; // replicator layers that will replicate the blocks into position @end @implementation TCViewController - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor lightGrayColor]; self.blockLayers = [NSMutableArray array]; self.replicatorLayers = [NSMutableArray array]; NSArray * dataBlocksData = @[@1,@2,@3,@1,@5,@2,@11,@14,@5,@12,@10,@3,@7,@6,@3,@4,@1]; __block CGFloat currentXPosition = 0.0f; [dataBlocksData enumerateObjectsUsingBlock:^(NSNumber * obj, NSUInteger idx, BOOL *stop) { currentXPosition += BLOCK_WIDTH + 2.0f; if (obj.integerValue == 0) return; // replicator layer for data blocks in a column CAReplicatorLayer * replicatorLayer = [[CAReplicatorLayer alloc] init]; replicatorLayer.frame = CGRectMake(currentXPosition, 0.0f, BLOCK_WIDTH, 200.0f); // single data block layer (must be new for each replicator layer. can't reuse existing layer) CALayer * blockLayer = [[CALayer alloc] init]; blockLayer.frame = CGRectMake(0, 200-BLOCK_HEIGHT, BLOCK_WIDTH, BLOCK_HEIGHT); blockLayer.backgroundColor = [UIColor whiteColor].CGColor; [replicatorLayer addSublayer:blockLayer]; replicatorLayer.instanceCount = obj.integerValue; replicatorLayer.instanceDelay = 0.1f; replicatorLayer.instanceTransform = CATransform3DMakeTranslation(0, -BLOCK_HEIGHT, 0); // negative height moves back up the column [self.blockLayers addObject:blockLayer]; [self.replicatorLayers addObject:replicatorLayer]; }]; } - (void)viewDidLayoutSubviews { // add replicator layers as sublayers [self.replicatorLayers enumerateObjectsUsingBlock:^(CAReplicatorLayer * obj, NSUInteger idx, BOOL *stop) { [self.view.layer addSublayer:obj]; }]; } - (void)viewDidAppear:(BOOL)animated { // add animations to each block layer [self.blockLayers enumerateObjectsUsingBlock:^(CALayer * obj, NSUInteger idx, BOOL *stop) { // position animation CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"]; animation.fromValue = @(-300); animation.toValue = @(0); animation.duration = 1.0f; animation.fillMode = kCAFillModeBoth; animation.removedOnCompletion = NO; animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]; [obj addAnimation:animation forKey:@"falling-block-animation"]; }]; } 

Finalmente, esta es la captura. Si no especifica ninguna function de timingFunction , una function Linear o una function EaseIn , las animaciones no se completan por completo. Como los bloques no caen completamente en su lugar. Parece que están cerca de terminar, pero no del todo. Solo un marco o dos apagado. -viewDidAppear: asignación de la function de temporización en -viewDidAppear: y obtendrás esta rareza:

introduzca la descripción de la imagen aquí

Adelante, pruébelo. Incluso intenté especificar una function con puntos de control ( enlace ) que son exactamente como una function EaseIn :

 // use control points for an EaseIn timing: animation.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.42 :0.00 :1.0 :1.0]; // or simply specify the ease in timing function: animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; 

Por alguna razón, las EaseOut y Default timing funcionan bien, pero una facilidad en la function de estilo parece tardar más en completarse, visualmente, ¿quizás debido a la longitud de la curva de interpolación?

He intentado muchas cosas para networkingucir el problema:

  1. No bucle sobre estas estructuras y usa solo una capa y una capa replicadora con la misma animation. Con eso quiero decir, anima solo una columna. Sin suerte. Looping no es el problema.
  2. Coloque el código de creación, subcapa-agregado y animation-agregado en -viewDidAppear . No parece hacer una diferencia si las animaciones se agregaron antes o después en el ciclo de vida de la vista.
  3. Uso de properties para mis estructuras. Ninguna diferencia.
  4. No uso de properties para mis estructuras. De nuevo, no hay diferencia.
  5. Se agregaron devoluciones de llamada para completar la animation para eliminar la animation a fin de dejar las columnas en el estado "final" correcto. Esto no fue bueno, pero vale la pena probarlo.
  6. El problema está presente en iOS 6 y 7.

Realmente quiero usar una function de synchronization EaseIn porque es la mejor animation para algo que se pone en marcha. También quiero usar CAReplicatorLayer porque hace exactamente lo que quiero hacer.

Entonces, ¿qué EaseIn con las EaseIn synchronization de EaseIn como las estoy usando? ¿Es esto un error?

Estoy de acuerdo en que esto parece ser un error en la combinación de CAReplicatorLayer y la function de synchronization kCAMediaTimingFunctionEaseIn.

Como una solución, sugiero usar una animation de fotogtwigs key con tres fotogtwigs key. Los primeros dos fotogtwigs key definen la animation que desea con el time de installation. El último fotogtwig key se define como idéntico al segundo fotogtwig key, pero le da a la animation más time para sentarse en el fotogtwig key final, lo que parece permitir que la capa replicadora genere la última capa replicada en el estado final.

Por lo tanto, mi sugerencia es:

 CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.y"]; // extra keyframe duplicating final state animation.values = @[@-300.0f, @0.0f, @0.0f]; // double length of original animation, with last half containing no actual animation animation.keyTimes = @[@0.0, @0.5, @1.0]; animation.duration = 2.0f; animation.fillMode = kCAFillModeBoth; animation.removedOnCompletion = NO; // ease-in for first half of animation; default after animation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]]; [obj addAnimation:animation forKey:@"falling-block-animation"];