简体   繁体   中英

How can I animate a UIColor in a CALayer?

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM