使用核心动画层动画视图
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
即可使界面恢复正常状态。
我的 NSImageView
s 设置动画,没有丢帧。我为您修改了示例并将其放在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 是核心动画机制,用于将多个层树操作批处理为渲染树的原子更新。
【讨论】:
似乎没有什么不同。以上是关于使用核心动画层动画视图的主要内容,如果未能解决你的问题,请参考以下文章