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

标签 ios objective-c macos core-animation

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

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

@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 忽略(或推迟)显式事务的任何提交,直到当前运行循环周期结束。查看消息日志,只要访问任何层的 presentationLayer,显式事务的 +[CATransaction commit] 就会变成空操作。

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

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

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

我得出结论,依赖 presentationLayer 来完成我想做的事情是错误的。

Apple 的文档文章 Core Animation Rendering Architecture清楚地表明 presentationLayer 具有层的所有值,因为它们当前对用户可见。在 runloop 循环结束(或外部通常隐式的 CATransaction 被刷新/提交)和所有层更改之前,presentationLayer 不会反射(reflect)任何有或没有动画的更改呈现给用户。

由于我的动画依赖于同一 runloop 周期内对层所做的更改,因此这些更改对用户尚不可见,因此无法通过 presentationLayer 获得。

-[CALayer display] 应该只访问层的值,而不是它的 presentationLayer 的值,如 Providing Layer Content 中的示例节目。 应该从用户当前看到的内容开始的动画可以使用 presentationLayer 的值作为 fromValue

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

关于ios - 为什么 CoreAnimation 似乎在缓存表示层副本?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14251095/

相关文章:

objective-c - SingleTouch-View 中的第二根手指触发 touchesMoved

objective-c - 如何与 iWatch 应用共享类(class)?

ios - 如何使用 AFNetworking 设置代理详细信息

macos - GCC Mac OS X 框架搜索路径 :/System/Library before/Library?

ios - 错误 ITMS-90717 : "Invalid App Store Icon"

ios - 如何从应用程序设置中的文本字段中退出键盘

ios - 奇怪的 NSURLSessionDownloadTask 行为通过蜂窝(不是 wifi)

ios - 如果我使用设备的 UUID,Apple 会拒绝我的应用程序吗?

c - 在 Mac OS X Lion 上获取 C 中的堆栈指针

c++ - Mac OS X 上的 g++ - CoreServices.h 错误