【问题标题】:How can I animate a UIColor in a CALayer?如何在 CALayer 中为 UIColor 设置动画?
【发布时间】:2018-10-09 18:46:42
【问题描述】:

我有一个自定义 CALayer,我在其中尝试使用 actionForKey 并遵循 this tutorial 启用某些属性的动画。

我有一个 CGFloat 属性,在动画块内时会完美改变,但我的另一个属性 UIColor 不会。

这是我的功能:

- (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];
}

颜色是使用CGContextSetFillColorWithColor 设置的,但我可以从日志中看到颜色只是从一种变化到另一种,没有任何插值。

有什么想法吗?

【问题讨论】:

    标签: ios objective-c core-graphics core-animation calayer


    【解决方案1】:

    最终结果很明显,我需要在我的 CALayer 中公开一个 CGColor 属性并为其设置动画。

    编辑:

    这里有一些代码,使用 UIViewCustomPropertyAnimation 项目作为基础。

    OCLayer.h 中添加一个新属性:

    @property (nonatomic) CGColorRef myColor;
    

    OCLayer.m 中添加@dynamic 指令:

    @dynamic myColor;
    

    并更新isCustomAnimKey

    + (BOOL)isCustomAnimKey:(NSString *)key {
        return [key isEqualToString:@"percent"] || [key isEqualToString:@"myColor"];
    }
    

    OCView.h 中添加相同的属性,但作为UIColor。这已经存在于我的项目中,因此不需要修改,这很棒,因为它没有破坏任何代码。

    @property (nonatomic, strong) UIColor *progressColor;
    

    主要更改将在 OCView.m 中,因为 getter 和 setter 需要从 CGColor 转换为 UIColor 并再次转换回来。

    - (void)setMyColor:(UIColor *)color {
        self.layer.myColor = color.CGColor;
    }
    
    - (UIColor*)myColor {
        return [UIColor colorWithCGColor: self.layer.myColor];
    }
    

    现在可以正常执行动画了:

    [UIView animateWithDuration:1.f animations:^{
        self.animView.myColor = [UIColor redColor];
    }];
    

    【讨论】:

    • @E.Coms 是的,添加了!
    • 我验证了但失败了。 CGColorRef 没有插值。
    • 没有看到你的代码我真的帮不上什么忙,我发布的就是我所做的,而且效果很好。
    【解决方案2】:

    这是我的代码,而不是答案。共有三个类:OCLayer、OCView 和 OCViewController。您可以看到“percent”的值在动画期间会发生变化,而“myColor”的值不会发生变化。

    @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
    

    【讨论】:

    • 您在 OCView.h 中使用 getter 和 setter 函数定义了您的 myColor,而不是使用自定义 getter 和 setter 的属性,这会有所不同吗?
    • 我换了OCView的界面,还是不行。我认为使用这种机制,CGFloat 可以工作,但 CGColorRef 不能。我可以为颜色的第四个组件设置动画,因为它们都是 CGFloat。无论如何,感谢您的宝贵时间。
    • 嗯,你是对的,我似乎也无法让它工作。我一直在做的项目实际上是github.com/MatiBot/MBCircularProgressBar,其中通过覆盖drawInContext在图层类中进行绘图。这是我的 PR:github.com/MatiBot/MBCircularProgressBar/pull/71 你可以看到我完全按照上面描述的方式完成了它,并且 100% 有效。
    猜你喜欢
    • 2011-04-25
    • 2019-04-18
    • 1970-01-01
    • 1970-01-01
    • 2012-11-01
    • 1970-01-01
    • 2014-04-19
    • 2013-10-06
    • 2010-10-05
    相关资源
    最近更新 更多