为什么CoreAnimation似乎正在缓存表示层副本?

dma*_*ach 7 macos core-animation objective-c ios

所以我们遇到了一些有趣的问题,看起来似乎是表示层的缓存,我们想知道其他人是否看到这个,以及我们可以做些什么变通办法.

我们有一些自己的属性,我们隐含动画.我们通过将它们声明为属性并动态合成它们来做到这一点:

@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];
}
Run Code Online (Sandbox Code Playgroud)

在大多数情况下,这工作正常,我们看到记录了适当的值.在第一种情况下,我们得到适当的隐式动画:

- (IBAction)doSomeGoo:(id)sender {
  sublayer_.gooValue = random();
}
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)

在第二种情况下,我们禁用操作,以便我们获得单个值更新,这很好:

- (IBAction)doSomeGoo2:(id)sender {
  long newValue = random();
  NSLog(@"newValue = %f", (float)newValue);
  [CATransaction begin];
  [CATransaction setDisableActions:YES];
  sublayer_.gooValue = newValue;
  [CATransaction commit];
}
Run Code Online (Sandbox Code Playgroud)

第一次通过:

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
Run Code Online (Sandbox Code Playgroud)

第二次通过:

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
Run Code Online (Sandbox Code Playgroud)

这是我们在进行有趣的事务之前调用[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];
}
Run Code Online (Sandbox Code Playgroud)

第一次通过:

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
Run Code Online (Sandbox Code Playgroud)

第二次通过:

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
Run Code Online (Sandbox Code Playgroud)

请注意添加presentationLayer调用如何使显示不获取当前值.

有关如何处理这个问题的建议,或者我是否完全误解了事情,这是预期的行为?

flu*_*nic 9

我没有访问presentationLayer,但今天仍然遇到了同样的问题.

每当UIScrollView出现滚动条时,某些动画就无法正常工作.我花了时间来弄清楚,它是涉及到一个问题CATransaction和访问presentationLayer.

在当前的runloop循环结束之前,访问忽略(或推迟)任何显式事务提交的presentationLayer原因CATransaction.查看消息日志,+[CATransaction commit]一旦presentationLayer访问任何层,显式事务就变为无操作.

我发现了两种方法可以使这些提交再次起作用,但会产生负面的副作用.

  1. 使用+[CATransaction flush]更新所有图层来刷新所有图层更改presentationLayer,+[CATransaction begin…commit]并按预期工作.
  2. 使用隐式事务提交会+[CATransaction commit]产生相同的结果.

但是一旦我这样做,整个应用程序中出现了其他随机问题.有些观点即使通过正确布局甚至drawRect:被称为隐形也是看不见的.更深入的我注意到这一点UIView,他们的支持CALayer经常变得不同步.

我得出的结论是,依靠presentationLayer我想做的事情我错了.

Apple的文档文章Core Animation Rendering Architecture清楚地表明presentationLayer了该层的所有值,因为它们当前对用户可见.presentationLayer在动态循环结束之前(或者通常隐式外部CATransaction被刷新/提交)并且所有层更改都呈现给用户,所以在有或没有动画的情况下进行的任何更改都不会反映出来.

由于我的动画依赖于在同一个runloop循环中对图层所做的更改,因此用户无法看到这些更改,因此无法通过presentationLayer.

-[CALayer display]应该只访问图层的值而不是它的值presentationLayer,如提供图层内容中的示例所示.应该从用户当前看到的动画开始的动画可以使用presentationLayer的值为fromValue.

仍然出现的任何其他问题很可能是由于以错误的方式使用渲染架构.我将重新考虑我的动画,以便他们使用图层的当前状态而不是presentationLayer's.