Search code examples
ioscore-animation

Why is implicit animation missing when I add a sublayer to a layer?


In a UIView subclass, I have this method:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch * aTouch = [touches anyObject];
    CGPoint loc = [aTouch locationInView:self];

    CALayer * layer = [CALayer layer];
    [layer setBackgroundColor: [[UIColor colorWithHue:(float)rand()/RAND_MAX saturation:1 brightness:1 alpha:1] CGColor]];
    [layer setFrame:CGRectMake(0, 0, 64, 64)];
    [layer setCornerRadius:7];
    [layer setPosition:loc];

    [layer setOpacity:0];

    [self.layer addSublayer:layer];


    CABasicAnimation * opacityAnim = [CABasicAnimation animationWithKeyPath:@"opacity"];
    opacityAnim.duration=2.42;
    opacityAnim.fromValue=[NSNumber numberWithFloat:0];
    opacityAnim.toValue=  [NSNumber numberWithFloat:1];
    opacityAnim.fillMode = kCAFillModeForwards;
    opacityAnim.timingFunction= [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    opacityAnim.removedOnCompletion=NO;
    opacityAnim.delegate=self;

//  explicit animation is working as expected
//  [layer addAnimation:opacityAnim forKey:@"opacityAnimation"];

//     Why isn't the implicit animation working ?
[layer setOpacity:1];
}

What am I missing ? I expect the CALayer layer's opacity to be implicitly animated with the last line in this method.

My solution

Here is how I solved the problem thanks to Duncan's answer.

-(CALayer *) layerFactory:(CGPoint) loc {
    CALayer * layer = [CALayer layer];
    [layer setBackgroundColor: [[UIColor colorWithHue:(float)rand()/RAND_MAX saturation:1 brightness:1 alpha:1] CGColor]];
    [layer setFrame:CGRectMake(0, 0, 64, 64)];
    [layer setCornerRadius:7];
    [layer setPosition:loc];
    [layer setOpacity:0];
    return layer;
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    UITouch * aTouch = [touches anyObject];
    CGPoint loc = [aTouch locationInView:self];


    [CATransaction begin];
    CALayer * layer = [self layerFactory:loc];
    [self.layer addSublayer:layer];
    [CATransaction commit];


    [CATransaction begin];
    [CATransaction setAnimationDuration:0.45];
    [layer setOpacity:1];
    [CATransaction commit];

}

You only need to put the creation of the layer, and the modifiction of the opacity in two different CATransaction blocks. However, moving the creation (not the adding) of the layer to the layerFactory method changes nothing to the situation.

I don't know whether it's the best solution, but it's working.


Solution

  • You can't create a layer and set it's opacity, then set the opacity to a new value in the same method and get implicit animation. Try putting your create/configure layer code inside a CATransaction begin/end block, and then the code to set the opacity to 1 inside another transaction begin/end block.

    I think that will work, but would have to try it to be sure.

    If that doesn't work, invoke the code to set the opacity to 1 using performSelector:withObject:afterDelay and a delay value of 0. That will let the system add your layer with an opacity of 0, then treat the change of opacity to 1 as a separate transaction.