7

因此,我们遇到了一些有趣的问题,似乎是表示层的缓存,我们想知道其他人是否看到了这一点,以及我们可以采取哪些解决方法。

我们有几个我们自己的属性,我们正在隐式动画。我们通过将它们声明为属性并动态合成它们来做到这一点:

@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

这是我们在执行有趣的事务之前调用 [CALayerpresentationLayer] 的第三种情况:

- (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 调用如何使显示无法获得当前值。

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

4

2 回答 2

10

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

每当UIScrollView出现 a 的滚动条时,一些动画就无法按预期工作。我花了几个小时才发现这是一个CATransactionpresentationLayer.

访问presentationLayer原因CATransaction以忽略(或推迟)任何显式事务的提交,直到当前的 runloop 循环结束。查看消息日志,对于任何层,+[CATransaction commit]只要访问显式事务,就会变成无操作。presentationLayer

我找到了两种让这些提交再次起作用的方法,但有负面影响。

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

但是一旦我这样做了,整个应用程序中就会出现其他随机问题。有些视图即使布局正确,甚至drawRect:被调用,也根本不可见。再深入一点,我注意到UIView's 和他们的支持CALayer经常变得不同步。

我得出的结论是我错了依靠presentationLayer我想做的事。

Apple 的文档文章Core Animation Rendering Architecture清楚地说明presentationLayer了图层的所有值,因为它们当前对用户可见。presentationLayer直到 runloop 循环结束(或者外部通常隐式CATransaction被刷新/提交)并且所有图层更改都呈现给用户之前,任何使用或不使用动画所做的更改都不会被反映。

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

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

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

于 2013-01-14T10:39:47.800 回答
1

我能够通过使用解决类似的问题[CATransaction setCompletionBlock:

这允许运行循环完成并更新presentationLayer

于 2014-05-30T03:06:08.657 回答