ActionForKey: How to get the new value for the property?

advertisements

In a CALayer subclass I'm working on I have a custom property that I want to animate automatically, that is, assuming the property is called "myProperty", I want the following code:

[myLayer setMyProperty:newValue];

To cause a smooth animation from the current value to "newValue".

Using the approach of overriding actionForKey: and needsDisplayForKey: (see following code) I was able to get it to run very nicely to simply interpolate between the old and and new value.

My problem is that I want to use a slightly different animation duration or path (or whatever) depending on both the current value and the new value of the property and I wasn't able to figure out how to get the new value from within actionForKey:

Thanks in advance

@interface ERAnimatablePropertyLayer : CALayer {
    float myProperty;
}
@property (nonatomic, assign) float myProperty;
@end
@implementation ERAnimatablePropertyLayer
@dynamic myProperty;

- (void)drawInContext:(CGContextRef)ctx {
     ... some custom drawing code based on "myProperty"
}

- (id <CAAction>)actionForKey:(NSString *)key {
    if ([key isEqualToString:@"myProperty"]) {
        CABasicAnimation *theAnimation = [CABasicAnimation animationWithKeyPath:key];
        theAnimation.fromValue = [[self presentationLayer] valueForKey:key];

        ... I want to do something special here, depending on both from and to values...

        return theAnimation;
        }
    return [super actionForKey:key];
    }

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


You need to avoid custom getters and setters for properties you want to animate.

Override the didChangeValueForKey: method. Use it to set the model value for the property you want to animate.

Don't set the toValue on the action animation.

@interface MyLayer: CALayer

    @property ( nonatomic ) NSUInteger state;

@end

-

@implementation MyLayer

@dynamic state;

- (id<CAAction>)actionForKey: (NSString *)key {

    if( [key isEqualToString: @"state"] )
    {
        CABasicAnimation * bgAnimation = [CABasicAnimation animationWithKeyPath: @"backgroundColor"];
        bgAnimation.fromValue = [self.presentationLayer backgroundColor];
        bgAnimation.duration  = 0.4;

        return bgAnimation;
    }

    return [super actionForKey: key];
}

- (void)didChangeValueForKey: (NSString *)key {

    if( [key isEqualToString: @"state"] )
    {
        const NSUInteger state = [self valueForKey: key];
        UIColor * newBackgroundColor;

        switch (state)
        {
            case 0:
                newBackgroundColor = [UIColor redColor];
                break;

            case 1:
                newBackgroundColor = [UIColor blueColor];
                break;

            case 2:
                newBackgroundColor = [UIColor greenColor];
                break;

            default:
                newBackgroundColor = [UIColor purpleColor];
        }

        self.backgroundColor = newBackgroundColor.CGColor;
    }

    [super didChangeValueForKey: key];
}

@end