I have a custom CALayer within which I'm trying to enable the animation of certain properties using actionForKey and following this tutorial.
I have a CGFloat
property that will change perfectly when inside an animation block but my other property, a UIColor
, will not.
Here's my function:
- (id<CAAction>)actionForKey:(NSString *)event {
if ([self presentationLayer] != nil && [[self class] isCustomAnimationKey:event]) {
id animation = [super actionForKey:@"backgroundColor"];
if (animation == nil || [animation isEqual:[NSNull null]]) {
[self setNeedsDisplay];
return [NSNull null];
}
[animation setKeyPath:event];
[animation setFromValue:[self.presentationLayer valueForKey:event]];
[animation setToValue:nil];
return animation;
}
return [super actionForKey:event];
}
The colour is being set using CGContextSetFillColorWithColor
but I can see from logs that the colour simply changes from one to the next without any of the interpolated values.
Any ideas?
It turned out to be obvious in the end, I needed to expose a CGColor
property in my CALayer and animate that instead.
Edit:
Here's some code for this, using the UIViewCustomPropertyAnimation project as a basis.
In OCLayer.h
add a new property:
@property (nonatomic) CGColorRef myColor;
In OCLayer.m
add the @dynamic directive:
@dynamic myColor;
And update isCustomAnimKey
:
+ (BOOL)isCustomAnimKey:(NSString *)key {
return [key isEqualToString:@"percent"] || [key isEqualToString:@"myColor"];
}
In OCView.h
add the same property but as a UIColor
. This already existed in my project so didn't require modification, which is great because it didn't break any code.
@property (nonatomic, strong) UIColor *progressColor;
The main changes would be in OCView.m
as the getter and setter need to convert from CGColor
to UIColor
and back again.
- (void)setMyColor:(UIColor *)color {
self.layer.myColor = color.CGColor;
}
- (UIColor*)myColor {
return [UIColor colorWithCGColor: self.layer.myColor];
}
The animation can now be carried out as normal:
[UIView animateWithDuration:1.f animations:^{
self.animView.myColor = [UIColor redColor];
}];
This is my code and not an answer. There are three classes: OCLayer, OCView and OCViewController. You can see the value of "percent" is changing during animation while the value of "myColor" won't.
@interface OCLayer : CALayer
@property (nonatomic) CGFloat percent;
@property (nonatomic) CGColorRef myColor;
@end
#import "OCLayer.h"
@implementation OCLayer
@dynamic percent;
@dynamic myColor;
- (id<CAAction>)actionForKey:(NSString *)key
{
if ([[self class] isCustomAnimKey:key])
{
id animation = [super actionForKey:@"backgroundColor"];
if (animation == nil || [animation isEqual:[NSNull null]])
{
[self setNeedsDisplay];
return [NSNull null];
}
[animation setKeyPath:key];
[animation setFromValue: [self.presentationLayer valueForKey:key]];
[animation setToValue : nil];
return animation;
}
return [super actionForKey:key];
}
- (id)initWithLayer:(id)layer
{
self = [super initWithLayer:layer];
if (self)
{
if ([layer isKindOfClass:[OCLayer class]])
{
self.percent = ((OCLayer *)layer).percent;
}
}
return self;
}
+ (BOOL)needsDisplayForKey:(NSString *)key
{
if ([self isCustomAnimKey:key]) return true;
return [super needsDisplayForKey:key];
}
+ (BOOL)isCustomAnimKey:(NSString *)key
{
return [key isEqualToString:@"percent"] || [key isEqualToString:@"myColor"];
}
@end
@interface OCView : UIView
@property (weak, nonatomic) IBOutlet UIView *percentView;
@property (weak, nonatomic) IBOutlet UILabel *label;
@property (nonatomic, strong) UIColor * myColor;
//- (UIColor*)myColor ;
//- (void)setMyColor:(UIColor *)color;
- (CGFloat )percent;
- (void)setPercent:(CGFloat )percent;
@end
#import "OCView.h"
#import "OCLayer.h"
@implementation OCView
- (void)displayLayer:(CALayer *)layer
{
CGFloat percent = [(OCLayer *)[self.layer presentationLayer] percent];
CGColorRef myColor = [(OCLayer *)[self.layer presentationLayer] myColor];
NSLog(@"%f", percent);
NSLog(@"%@", myColor);
self.percentView.backgroundColor = [[UIColor alloc]initWithCGColor: myColor];
self.label.text = [NSString stringWithFormat:@"%.0f", floorf(percent)];
}
+ (Class)layerClass
{
return [OCLayer class];
}
- (void)setPercent:( CGFloat )percent
{
((OCLayer *)self.layer).percent = percent;
}
- (CGFloat )percent
{
return ((OCLayer *)self.layer).percent;
}
- (void)setMyColor:(UIColor *)color {
((OCLayer *)self.layer).myColor = color.CGColor;
}
- (UIColor*)myColor {
return [UIColor colorWithCGColor: ((OCLayer *)self.layer).myColor];
}
@end
@interface OCViewController : UIViewController
@property (weak, nonatomic) IBOutlet OCView *animView;
@end
#import "OCViewController.h"
#import "OCLayer.h"
@interface OCViewController ()
@end
@implementation OCViewController
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
self.animView.percent = 1;
self.animView.myColor = [UIColor whiteColor];
[UIView animateWithDuration:3.0
animations:^{
self.animView.percent = 20;
self.animView.myColor = [UIColor redColor];
}];
}
- (void)viewDidLoad {
[super viewDidLoad];
}
@end