For Apple’s iOS, below is the code to implicitly animate a custom property, using float as an example, in a subclass of CALayer:
/* MyLayer.m */
@interface MyLayer() {
BOOL _isPresenting; /* TRUE for presentation layer */
}
@property (nonatomic) float property; /* normally defined in MyLayer.h */
@end
@implementation MyLayer
@synthesize property = _property;
- (void)setProperty:(float)property {
if (_isPresenting) {
NSLog(@"Presenting layer");
_property = property;
return;
}
NSLog(@"Setting value");
if ([self animationForKey:@"animateProperty"]) {
_property = [[self presentationLayer] property];
[self removeAnimationForKey:@"animateProperty"];
}
CABasicAnimation *a = [CABasicAnimation animationWithKeyPath:@"property"];
[a setFromValue:[NSNumber numberWithFloat:_property]];
[a setToValue:[NSNumber numberWithFloat:property]];
/* Set other animation properties */
[self addAnimation:a forKey:@"animateProperty"];
_property = property;
}
+ (BOOL)needsDisplayForKey:(NSString *)key {
if ([key isEqualToString:@"property"]) {
return YES;
}
return [super needsDisplayForKey:key];
}
- (id)init {
if (self = [super init]) {
_property = 10; /* initial value */
}
return self;
}
- (id)initWithLayer:(id)layer {
if (self = [super initWithLayer:layer]) {
MyLayer *l = (MyLayer *)layer;
_isPresenting = TRUE;
_property = l.property;
}
return self;
}
- (void)drawInContext:(CGContextRef)ctx {
NSLog(@"%f", _property);
/* Add drawing code */
}
@end
Then, setting the custom property with:myLayer.property = 50;
gives the following in the log:Setting value
Presenting layer
18.953653
Presenting layer
25.723625
Presenting layer
..
..
..
Presenting layer
49.999870
50.000000
50.000000
The animation is working, but it cannot be overridden. It is time to change that by adding:+ (id<CAAction>)defaultActionForKey:(NSString *)key {
if ([key isEqualToString:@"property"]) {
CABasicAnimation *a = [CABasicAnimation animationWithKeyPath:@"property"];
[a setValue:@"defaultAction" forKey:@"animationType"];
return a;
}
return [super defaultActionForKey:key];
}
and modifying the setter to:- (void)setProperty:(float)property {
if (_isPresenting) {
NSLog(@"Presenting layer");
_property = property;
return;
}
NSLog(@"Setting value");
if ([self animationForKey:@"animateProperty"]) {
_property = [[self presentationLayer] property];
[self removeAnimationForKey:@"animateProperty"];
}
CABasicAnimation *a = (CABasicAnimation *)[self actionForKey:@"property"];
if (a) {
if ([[a valueForKey:@"animationType"] isEqualToString:@"defaultAction"]) {
[a setFromValue:[NSNumber numberWithFloat:_property]];
[a setToValue:[NSNumber numberWithFloat:property]];
/* Set other animation properties */
}
[self addAnimation:a forKey:@"animateProperty"];
}
_property = property;
}
Now, all is good.