所以我们遇到了一些有趣的问题,似乎是表示层的缓存,我们想知道其他人是否看到了这一点,以及我们可以做些什么解决方法。
我们有一些我们自己的属性,我们隐式动画。我们通过将它们声明为属性并动态合成它们来做到这一点:
@interface GOOLayer : CALayer
@property float gooValue;
@end
NSString *const kGOOLayerValueKey = @"gooValue";
@implementation GOOLayer
@dynamic gooValue;
- (id)initWithLayer:(id)layer {
if ((self = [super initWithLayer:layer])) {
id value = [layer valueForKey:kGOOLayerValueKey];
[self setValue:value forKey:kGOOLayerValueKey];
}
return self;
}
- (id<CAAction>)actionForKey:(NSString *)event {
id <CAAction> action = [super actionForKey:event];
if (action == nil && [event isEqual:kGOOLayerValueKey]) {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:event];
animation.fromValue = [self.presentationLayer valueForKey:event];
animation.duration = [CATransaction animationDuration];
return animation;
}
return action;
}
- (void)display {
GOOLayer *presoLayer = [self presentationLayer];
NSLog(@"Display GooValue: %f", presoLayer.gooValue);
}
+ (BOOL)needsDisplayForKey:(NSString *)key {
if ([key isEqual:kGOOLayerValueKey]) {
return YES;
}
return [super needsDisplayForKey:key];
}
在大多数情况下,这工作正常,我们会看到记录了适当的值。
在第一种情况下,我们得到了适当的隐式动画:
- (IBAction)doSomeGoo:(id)sender {
sublayer_.gooValue = random();
}
输出:
2012-12-19 10:10:41.440 CoreAnimation[94149:c07] Display GooValue: 1804289408.000000
2012-12-19 10:10:41.502 CoreAnimation[94149:c07] Display GooValue: 1804289408.000000
...
2012-12-19 10:10:41.739 CoreAnimation[94149:c07] Display GooValue: 898766464.000000
2012-12-19 10:10:41.757 CoreAnimation[94149:c07] Display GooValue: 846930880.000000
在第二种情况下,我们禁用操作,所以我们得到一个很好的单值更新:
- (IBAction)doSomeGoo2:(id)sender {
long newValue = random();
NSLog(@"newValue = %f", (float)newValue);
[CATransaction begin];
[CATransaction setDisableActions:YES];
sublayer_.gooValue = newValue;
[CATransaction commit];
}
第一次通过:
2012-12-19 10:11:16.208 CoreAnimation[94149:c07] newValue = 1714636928.000000
2012-12-19 10:11:16.209 CoreAnimation[94149:c07] Display GooValue: 1714636928.000000
第二次通过:
2012-12-19 10:11:26.258 CoreAnimation[94149:c07] newValue = 1957747840.000000
2012-12-19 10:11:26.259 CoreAnimation[94149:c07] Display GooValue: 1957747840.000000
这是我们在进行有趣的交易之前调用 [CALayer presentationLayer] 的第三种情况:
- (IBAction)doSomeGoo3:(id)sender {
GOOLayer *presoLayer = sublayer_.presentationLayer;
long newValue = random();
NSLog(@"presoLayer.gooValue = %f newValue = %f", presoLayer.gooValue, (float)newValue);
[CATransaction begin];
[CATransaction setDisableActions:YES];
sublayer_.gooValue = newValue;
[CATransaction commit];
}
第一次通过:
2012-12-19 10:12:12.505 CoreAnimation[94149:c07] presoLayer.gooValue = 1957747840.000000 newValue = 424238336.000000
2012-12-19 10:12:12.505 CoreAnimation[94149:c07] Display GooValue: 1957747840.000000
第二次通过:
2012-12-19 10:12:13.905 CoreAnimation[94149:c07] presoLayer.gooValue = 424238336.000000 newValue = 719885376.000000
2012-12-19 10:12:13.906 CoreAnimation[94149:c07] Display GooValue: 424238336.000000
请注意添加presentationLayer 调用如何使其显示不获取当前值。
关于如何处理这个问题的任何建议,或者我是否完全误解了事情,这是预期的行为?
最佳答案
我没有访问 presentationLayer
,但今天仍然遇到同样的问题。
每当 UIScrollView
的滚动条出现时,某些动画就无法按预期工作。我花了几个小时才发现这是一个与 CATransaction
和访问 presentationLayer
相关的问题。
访问 presentationLayer
会导致 CATransaction
忽略(或推迟)任何显式事务的提交,直到当前 runloop 循环结束。查看消息日志,一旦为任何层访问了 +[CATransaction commit]
,显式事务的 presentationLayer
就变成了空操作。
我找到了两种使这些提交再次起作用的方法,但会产生负面影响。
+[CATransaction flush]
刷新所有层更改会更新所有层的 presentationLayer
并使 +[CATransaction begin…commit]
按预期工作。 +[CATransaction commit]
提交隐式事务会产生相同的结果。 但是当我这样做时,整个应用程序中出现了其他随机问题。有些 View 即使布局正确,甚至调用了
drawRect:
也根本不可见。深入我注意到
UIView
和他们的支持 CALayer
经常变得不同步。我得出的结论是,依靠
presentationLayer
来完成我想做的事情是错误的。Apple 的文档文章 Core Animation Rendering Architecture 明确指出
presentationLayer
具有图层的所有值,因为它们当前对用户可见。在 runloop 循环结束(或外部通常隐含的 presentationLayer
被刷新/提交)并且所有层更改都呈现给用户之前,CATransaction
不会反射(reflect)任何有或没有动画的更改。由于我的动画依赖于在同一个 runloop 循环中对图层所做的更改,因此这些更改对用户来说尚不可见,因此无法通过
presentationLayer
获得。-[CALayer display]
应该只访问层的值,而不是其 presentationLayer
的值,如 Providing Layer Content 中的示例所示。应该从用户当前看到的内容开始的动画可以使用
presentationLayer
的值作为 fromValue
。仍然出现的任何其他问题很可能是由于以错误的方式使用渲染架构。
我将重新考虑我的动画,以便它们使用图层的当前状态而不是
presentationLayer
的。关于ios - 为什么 CoreAnimation 似乎在缓存表示层副本?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14251095/