CALAyer子类中的非动画属性



我已经定义了CALayer的一个子类,具有这里讨论的animatable属性。现在,我想添加另一个(非动画)属性到该层,以支持其内部记账。

我在drawInContext:中设置了新属性的值,但我发现当下一次调用时它总是重置为0。这是因为核心动画假设该属性也用于动画,并且它将其值"动画"为常数0,缺乏进一步的说明吗?在任何情况下,我怎么能添加真正的非动画属性的子类CALayer ?

我已经找到了一个初步的解决方案,它使用全局CGFloat _property而不是@property (assign) CGFloat property,但更愿意使用正常的属性语法。

更新1

这就是我如何在MyLayer.m中定义属性:

@interface MyLayer()
@property (assign) CGFloat property;
@end

我是这样在drawInContext:的末尾给它赋值的:

self.property = nonZero;

属性在drawInContext:开头读取,如下所示:

NSLog(@"property=%f", self.property);

更新2

也许这是导致问题的原因(从这个示例继承的代码)?

- (id)actionForKey:(NSString *) aKey {
    if ([aKey isEqualToString:@"someAnimatableProperty"]) {
       CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:aKey];
       animation.fromValue = [self.presentationLayer valueForKey:aKey];
       return animation;
    }
    return [super actionForKey:aKey]; // also applies to my "property"
}

要从绘图方法中访问您的标准属性,在动画期间,您需要做一些修改。

执行初始化

当CoreAnimation执行你的动画时,会创建你的图层的阴影副本,并且每个副本将在不同的帧中渲染。为了创建这样的副本,它调用-initWithLayer:。来自Apple的文档:

如果你正在实现一个自定义层子类,你可以覆盖这个方法,并使用它来复制实例变量的值到新的对象。子类应该始终调用超类实现。

因此,您需要实现-initWithLayer:,并使用它在新实例上手动复制属性的值,如下所示:
- (id)initWithLayer:(id)layer
{
    if ((self = [super initWithLayer:layer])) {
        // Check if it's the right class before casting
        if ([layer isKindOfClass:[MyCustomLayer class]]) {
            // Copy the value of "myProperty" over from the other layer
            self.myProperty = ((MyCustomLayer *)layer).myProperty;
        }
    }
    return self;
}

通过模型层访问属性

无论如何,的复制发生在动画开始之前:您可以通过向-initWithLayer:添加NSLog调用来看到这一点。据CoreAnimation所知,你的属性将永远为零。此外,它创建的副本是readonly,如果您试图从-drawInContext:中设置self.myProperty,当在其中一个表示副本上调用该方法时,您会得到一个异常:

*** Terminating app due to uncaught exception 'CALayerReadOnly', reason:  
    'attempting to modify read-only layer <MyLayer: 0x8e94010>' ***

你应该写

而不是设置self.myProperty
self.modelLayer.myProperty = 42.0f

as modelLayer将把引用到原始MyCustomLayer实例,并且所有表示副本共享相同的模型。注意,在读取变量时也必须这样做,而不仅仅是在设置变量时。为了完整起见,还应该提到属性presentationLayer,它返回当前显示的图层(副本)。

最新更新