I have a subclass of CALayer
with a custom property, declared as such:
@interface MyLayer : CALayer
@property (nonatomic,retain) NSNumber *customValue;
@end
@implementation MyLayer
@synthesize customValue = _customValue;
@end
I want to animate this property inside of an explicit CATranasction
, so i set up a delegate with the actionForLayer:forKey:
method implemented which returns an animation, however any changes to someMyLayerInstance.customValue
inside of [CATransaction begin] ... [CATransaction end]
do not result in actionForLayer:forKey
getting called with a corresponding 'key' value.
However, nuking the property on MyLayer
and making changes to myLayerInstance
by calling setValue:forKey:
does result in actionForLayer:forKey:
getting called.
It appears that this is because CALayer
does some mojo for key/value coding for undefined properties. How can I recreate this mojo so that I can declare properties on MyLayer
, but still have them be observed by the animation delegate?
The most important thing is that you need to implement all CALayer
accessors using @dynamic
. Do not use @synthesize
and do not implement the accessors directly. CALayer
generates all its own property handlers (as you've indirectly discovered), and you need to let those be used.
You also need to let CALayer
know that this property is display-impacting (which you may have already done given your other comments). If you haven't, you do this by implementing +needsDisplayForKey:
and returning YES
for your key.
Here's an example of a CALayer
that animates a custom property (taken from Chapter 7 of iOS 5 Programming Pushing the Limits. The full sample code is available at the Wiley site.) This example implements actionForKey:
in the layer, but you can still implement that part in the delegate if you prefer.
@implementation CircleLayer
@dynamic radius;
...
+ (BOOL)needsDisplayForKey:(NSString *)key {
if ([key isEqualToString:@"radius"]) {
return YES;
}
return [super needsDisplayForKey:key];
}
- (id < CAAction >)actionForKey:(NSString *)key {
if ([self presentationLayer] != nil) {
if ([key isEqualToString:@"radius"]) {
CABasicAnimation *anim = [CABasicAnimation
animationWithKeyPath:@"radius"];
anim.fromValue = [[self presentationLayer]
valueForKey:@"radius"];
return anim;
}
}
return [super actionForKey:key];
}
@end