使用核心动画层动画视图

Kev*_*tre 8 macos cocoa core-animation core-graphics objective-c

我有一个NSWindow包含NSView'Wants Core Animation Layer'的功能.然后视图包含许多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];
}
Run Code Online (Sandbox Code Playgroud)

Nat*_*ror 10

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

使用该frame财产也可能导致经济放缓.我真的很想在这样的情况下利用这个transform属性CALayer,你正在做一个"开放"的动画.您可以在IB(或代码中)的最终位置布置图像,在窗口可见之前,将其变换修改为动画的起始位置.然后,您只需将所有转换重置为CATransform3DIdentity以使接口进入正常状态.

我的<plug type ="无耻">即将推出的Core动画书</ plug>中有一个例子,它与你想要做的非常相似.它NSImageView同时动画30 秒,没有丢帧.我为你修改了这个例子并把它放在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];
}
Run Code Online (Sandbox Code Playgroud)

将变换动画回到身份

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

如果您想查看该视频,我还会在YouTube上播放视频的视频.