2
votes

There are a lot of related questions, but this situation does not seem to be addressed by any the existing questions.

I have created a view with a custom layer so that one of the properties can be animated. Using the CABasicAnimation class, the animation works correctly.

However, I need a little more control over the animation, such as the ease in and ease out and sequential animations and tried to switch to using block animations. However, when I do that, the animation completes immediately rather than animating over time.

How can I get this block animation to work correctly?

Working animation code:

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"inputValue"];
animation.duration = DEFAULT_ANIMATION_DURATION;

if (flipped) {
    animation.fromValue = [NSNumber numberWithDouble:0.0];
    animation.toValue = [NSNumber numberWithDouble:1.0];
    self.myLayer.inputValue = 1.0;
} else {
    animation.fromValue = [NSNumber numberWithDouble:1.0];
    animation.toValue = [NSNumber numberWithDouble:0.0];
    self.myLayer.inputValue = 0.0;
}

[self.layer addAnimation:animation forKey:@"animateInputValue"];

Animation that incorrectly completes immediately, but finished is YES:

[UIView animateWithDuration:10.0 delay:0.0 options:0 animations:^{
    self.myLayer.inputValue = 1.0;
} completion:^(BOOL finished) {
    NSLog(@"done %@", finished?@"and finished":@", but not finished");
}];

The CALayer being animated:

#import "UViewLayer.h"
#import "YoYouStyleKit.h"

@implementation UViewLayer

+ (BOOL)needsDisplayForKey:(NSString *)key {
    if( [key isEqualToString:@"inputValue"] )
        return YES;
    return [super needsDisplayForKey:key];
}

- (void)setInputValue:(CGFloat)inputValue {
    _inputValue = inputValue;
    [self setNeedsDisplay];
}

- (void)drawInContext:(CGContextRef)context {
    UIGraphicsPushContext(context);
    [YoYouStyleKit drawUShapeWithFrame:self.bounds input:self.inputValue];
    UIGraphicsPopContext();
}

Adding @dynamic inputValue; in the custom layer seems to make no difference.

2
Are you sure that when you initialize animation, self is not nil and self.myLayer is not nil ? That is usually causing UIView animation to finish immediately.Grzegorz Krukowski

2 Answers

1
votes

Do not mix UIKit and Core Animation animations.

Implement like this:

[CATransaction begin];

[CATransaction setCompletionBlock:^
{
    NSLog(@"done");
}];

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"inputValue"];
animation.duration = DEFAULT_ANIMATION_DURATION;

if (flipped)
{
    animation.fromValue = [NSNumber numberWithDouble:0.0];
    animation.toValue = [NSNumber numberWithDouble:1.0];
    self.myLayer.inputValue = 1.0;
} 
else 
{
    animation.fromValue = [NSNumber numberWithDouble:1.0];
    animation.toValue = [NSNumber numberWithDouble:0.0];
    self.myLayer.inputValue = 0.0;
}

[self.layer addAnimation:animation forKey:@"animateInputValue"];

[CATransaction commit];
1
votes

In addition to Leo Natan's answer, as mentioned in the Apple docs :

Custom layer objects ignore view-based animation block parameters and use the default Core Animation parameters instead.

What's more tricky is that you can animate the UIView's own layer properties, provided you change one of the animatable properties.

For custom properties like your layer inputValue, you can provide the CABasicAnimation in your layer (id<CAAction>)actionForKey:(NSString *)key but it will not use the UIView animation parameters (duration...).

This animation will play when you change the layer property in the UIView block animation, but it will also play when you simply set the value.

The code provided by Leo Natan is the easiest when to animate your layer from the UIView.