我已经定义了一个CALayer
的子类,其中包含一个动画属性,如here所述。我现在想为该层添加另一个(不可动画的)属性以支持其内部簿记。
我在drawInContext:
中设置了新属性的值,但我发现在下一次调用时它总是重置为0。是这样的,因为Core Animation假设这个属性也用于动画,并且它动画"动画"它在常数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
也许这是导致问题(代码继承自this样本)?
- (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"
}
答案 0 :(得分:9)
要从绘图方法中访问标准属性,在动画期间,您需要进行一些修改。
当CoreAnimation执行动画时,会创建图层的阴影副本,每个副本将在不同的帧中呈现。要创建此类副本,它会调用-initWithLayer:
。
来自Apple's documentation:
如果要实现自定义图层子类,则可以覆盖此方法并使用它将实例变量的值复制到新对象中。子类应始终调用超类实现。
因此,您需要实现-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;
}
副本,无论如何,在动画开始之前发生:您可以通过向NSLog
添加-initWithLayer:
来查看此内容。因此,就CoreAnimation而言,您的财产将始终为零。此外,它创建的副本是 readonly ,如果您尝试在self.myProperty
内设置-drawInContext:
,当在其中一个演示文稿副本上调用该方法时,您会得到一个例外:
*** Terminating app due to uncaught exception 'CALayerReadOnly', reason:
'attempting to modify read-only layer <MyLayer: 0x8e94010>' ***
您应该编写
,而不是设置self.myProperty
self.modelLayer.myProperty = 42.0f
因为modelLayer
会将引用到原始MyCustomLayer
实例,并且所有演示文稿副本共享相同的模型。请注意,在读取变量时也必须执行此操作,而不仅仅是在设置时。为了完整性,还应该提及属性presentationLayer
,而不是返回正在显示的图层的当前(副本)。