我已经定义了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
,它返回当前显示的图层(副本)。