Reputation: 3726
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?
Upvotes: 0
Views: 439
Reputation: 11531
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
Upvotes: 0
Reputation: 3726
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];
}];
Upvotes: 1