使用核心动画层动画视图

Posted

技术标签:

【中文标题】使用核心动画层动画视图【英文标题】:Animating Views with Core Animation Layer 【发布时间】:2011-01-25 01:02:22 【问题描述】:

我有一个 NSWindow 包含一个 NSView 并启用了“想要核心动画层”。然后视图包含许多NSImageView,它们最初被动画到位置。当我运行动画时,它非常缓慢并且丢失了大部分帧。但是,如果我禁用“想要核心动画层”,则动画效果很好。我需要核心动画层,但不知道如何让它充分发挥作用。

我可以做些什么来解决性能问题吗?

代码如下:

// AppDelegate
NSRect origin = ...;
NSTimeInterval d = 0.0;
for (id view in views)

    [view performSelector:@selector(animateFrom:) withObject:origin afterDelay:d];
    d += 0.05f;


// NSImageView+Animations
- (void)animateFrom:(NSRect)origin
   
    NSRect original = self.frame;
    [self setFrame:origin];
    [NSAnimationContext beginGrouping];
    [[NSAnimationContext currentContext] setDuration:0.20f];
    [[self animator] setFrame:original];
    [NSAnimationContext endGrouping];

【问题讨论】:

【参考方案1】:

NSTimer 可能会扼杀你的表现。 Core Animation 对通过CAMediaTiming 协议控制动画的时间有丰富的支持,你应该在你的应用程序中利用它。不要使用动画代理和NSAnimationContext,而是直接使用Core Animation。如果为每个图像创建一个CABasicAnimation 并设置其beginTime,它将延迟动画的开始。此外,要使延迟按您想要的方式工作,您必须将每个动画包装在 CAAnimationGroup 中,并将其 duration 设置为整个动画的总时间。

使用frame 属性也可能导致速度变慢。我真的很喜欢利用CALayer 上的transform 属性,在这种情况下,您正在制作“打开”动画。您可以在 IB(或代码)中将图像布置在它们的最终位置,并在窗口变得可见之前,将它们的变换修改为动画的起始位置。然后,您只需将所有转换重置为 CATransform3DIdentity 即可使界面恢复正常状态。

我的 即将出版的 Core Animation 书 中有一个示例,它与您尝试做的非常相似.它同时为 30 个NSImageViews 设置动画,没有丢帧。我为您修改了示例并将其放在github 上。这些是最相关的代码,去掉了无关的 UI 内容:

将图层转换到它们的起始位置

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
  // ... SNIP ... //
  //Start with all of the images at the origin
  [CATransaction begin];
  [CATransaction setDisableActions:YES];
  for (CALayer *imageLayer in [[[self imageContainer] layer] sublayers]) 
    CGPoint layerPosition = [layer position];
    CATransform3D originTransform = CATransform3DMakeTranslation(20.f - layerPosition.x, -layerPosition.y, 0.f);
    [imageLayer setTransform:originTransform];
  
  [CATransaction commit];

将变换动画化回身份

- (IBAction)runAnimation:(id)sender 
  CALayer *containerLayer = [[self imageContainer] layer];

  NSTimeInterval delay = 0.f;
  NSTimeInterval delayStep = .05f;
  NSTimeInterval singleDuration = [[self durationStepper] doubleValue];
  NSTimeInterval fullDuration = singleDuration + (delayStep * [[containerLayer sublayers] count]);

  for (CALayer *imageLayer in [containerLayer sublayers]) 
    CATransform3D currentTransform = [[imageLayer presentationLayer] transform];

    CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform"];
    anim.beginTime = delay;
    anim.fromValue = [NSValue valueWithCATransform3D:currentTransform];
    anim.toValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
    anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    anim.fillMode = kCAFillModeBackwards;
    anim.duration = singleDuration;

    CAAnimationGroup *group = [CAAnimationGroup animation];
    group.animations = [NSArray arrayWithObject:anim];
    group.duration = fullDuration;

    [imageLayer setTransform:CATransform3DIdentity];
    [imageLayer addAnimation:group forKey:@"transform"];
    delay += delayStep;
  

如果您想查看,我也有一个示例的video on YouTube。

【讨论】:

这看起来很有希望!可能需要我一点时间阅读所有内容,但视频演示看起来很棒。【参考方案2】:

您是否尝试在 CATransaction 中批处理所有内容?

[CATransaction begin];
for ...
[CATransaction commit];

CATransaction 是核心动画机制,用于将多个层树操作批处理为渲染树的原子更新。

【讨论】:

似乎没有什么不同。

以上是关于使用核心动画层动画视图的主要内容,如果未能解决你的问题,请参考以下文章

在 Layer-Backed 视图上使用核心动画

iOS开发UI篇—核心动画(转场动画和组动画)

转场/视图转换后 iOS 核心动画失败

iOS核心动画高级技巧 - 8

iOS核心动画

UIkit 动画和核心动画