核心动画CALayer面具动画表现

时间:2022-07-13 16:52:06

We wanted to use a UITabBar in our iPhone app, but with one exception: we have a "sync" button which I wanted to rotate while the sync operation is happening.

我们想在我们的iPhone应用程序中使用UITabBar,但有一个例外:我们有一个“同步”按钮,我想在同步操作发生时旋转。

核心动画CALayer面具动画表现

Unfortunately this meant having to create a custom tab bar, but that's neither here nor there: the animation I implemented using Core Animation looks awesome. The problem is that while animating, it adversely affects the performance of everything else using animation on the screen: UITableView scrolling, MKMapView panning and pin drops, etc. My test device is an iPhone 4.

不幸的是,这意味着必须创建一个自定义标签栏,但这既不在这里也不在那里:我使用Core Animation实现的动画看起来很棒。问题在于,在动画时,它会对屏幕上使用动画的所有其他内容的性能产生负面影响:UITableView滚动,MKMapView平移和引脚丢弃等。我的测试设备是iPhone 4。

The problem seems to be how I've implemented the tab bar - I wanted to achieve something very similar to UITabBar, where you just supply a PNG for the icon and it uses the alpha channel to create the normal and highlighted states by masking a background image. I accomplished this with CALayer's mask property:

问题似乎是我如何实现标签栏 - 我想要实现与UITabBar非常相似的东西,你只需要为图标提供PNG,它使用alpha通道通过屏蔽背景来创建正常和突出显示的状态图片。我用CALayer的mask属性完成了这个:

// Inside a UIView subclass' init method...

// Create the mask layer by settings its contents as our PNG icon.
CALayer *maskLayer = [CALayer layer];
maskLayer.frame = CGRectMake(0, 0, 31, 31);
maskLayer.contentsGravity = kCAGravityCenter;
maskLayer.contentsScale = [[UIScreen mainScreen] scale];
maskLayer.rasterizationScale = [[UIScreen mainScreen] scale];
maskLayer.contents = (id)symbolImage.CGImage;
maskLayer.shouldRasterize = YES;
maskLayer.opaque = YES;

fgLayer = [[CALayer layer] retain];
fgLayer.frame = self.layer.frame;
fgLayer.backgroundColor = [UIColor colorWithImageNamed:@"tabbar-normal-bg.png"].CGColor;
fgLayer.mask = maskLayer; // Apply the mask
fgLayer.shouldRasterize = YES;
fgLayer.opaque = YES;

[self.layer addSublayer:fgLayer];

(Note: in the screenshot above you can see I've also added a shadow layer, but for simplicity I removed that from the code. I remove the shadow layer from the sync icon when it is animating, so it shouldn't be relevant.)

(注意:在上面的屏幕截图中你可以看到我还添加了一个阴影层,但为了简单起见,我从代码中删除了它。我在动画时从同步图标中删除了阴影层,所以它不应该是相关的。)

To animate, I simply rotate the mask layer:

要设置动画,我只需旋转遮罩层:

- (void)startAnimating {
    CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath: @"transform"];
    CATransform3D transform = CATransform3DMakeRotation(RADIANS(179.9), 0.0, 0.0, 1.0);
    animation.toValue = [NSValue valueWithCATransform3D:transform];
    animation.duration = 5;
    animation.repeatCount = 10000;
    animation.removedOnCompletion = YES;
    [fgLayer.mask addAnimation:animation forKey:@"rotate"]; // Add animation to the mask
}

So this all works great except for performance. You can see I've already tried tips that turned up on Google about rasterizing layers / making them opaque - hasn't helped.

所以这一切都很有效,除了性能。你可以看到我已经尝试了关于栅格化图层/使它们变得不透明的Google上的提示 - 没有帮助。

I think I've identified the mask layer as being the culprit. When I take out the mask layer and just rotate the fgLayer instead of its mask, performance is wonderful, though it's certainly not the affect I'm going for:

我想我已经确定掩模层是罪魁祸首。当我拿出面具层并旋转fgLayer而不是它的面具时,性能很棒,虽然它肯定不是我想要的影响:

核心动画CALayer面具动画表现

Performance is also just as bad as before if I rotate the fgLayer instead of the mask while the mask is applied.

如果在应用蒙版时旋转fgLayer而不是蒙版,性能也和以前一样糟糕。

So if having to recomposite the mask each frame of the animation is the slow down, are there any other techniques I can use to achieve a similar affect that will have better performance? Using a path instead of an image for the mask layer? Or am I going to have to drop down to OpenGL or something to get good performance?

因此,如果必须重新组合掩模,动画的每一帧都会变慢,那么我是否可以使用其他任何技术来实现具有更好性能的类似效果?使用路径而不是图像作为遮罩层?或者我将不得不下载到OpenGL或其他什么来获得良好的性能?

UPDATE: further reinforcing the idea that the mask is the slowdown, my coworker suggested trying to rotate a CALayer with just the image as the contents -- so similar to my example above w/o a mask -- and performance was also good that way. So I can really only do a solid color like that (no gradient), but it may be a good interim solution. I'd still love to achieve rotating a mask with good performance though, so suggestions welcome :)

更新:进一步强化了面具减速的想法,我的同事建议尝试用图像作为内容来旋转CALayer - 这与上面没有面具的例子类似 - 而且表现也很好。所以我真的只能做那样的纯色(没有渐变),但它可能是一个很好的临时解决方案。我仍然喜欢旋转具有良好性能的面罩,所以建议欢迎:)

2 个解决方案

#1


15  

Brent,

布伦特,

Why do you need to use a layer mask? Can't you just convert your mask layer to a sublayer? You would just need to make sure your image had the proper alpha and you would use it's CGImageRef as that layer's contents.

为什么需要使用图层蒙版?你不能只是将你的掩码层转换为子层吗?您只需要确保您的图像具有正确的alpha,并且您将使用它的CGImageRef作为该图层的内容。

One other thing. I haven't figured out why yet, but I've also noticed performance issues when I apply shouldRasterize on each layer instead of just the top layer. You might see if removing the reference to setShouldRasterize:YES in your mask layer helps at all.

还有一件事。我还没弄清楚原因,但是当我在每一层上应用shouldRasterize而不仅仅是顶层时,我也注意到了性能问题。您可能会看到在掩码图层中删除对setShouldRasterize:YES的引用是否有帮助。

#2


7  

One approach would be to create a CAShapeLayer to use as your mask—you'd have a fair bit of work cut out for you making a version of your “sync” icon with Bézier paths, but shape layers incur a much lower performance cost per-transformation than bitmap ones. Unfortunately, you can't be sure that the rotation is where the performance issue is coming from—it could very well be the masking causing most of the lag, in which case you'll have done all that vectorizing for little benefit.

一种方法是创建一个CAShapeLayer作为你的面具 - 你需要做一些工作来制作一个带有Bézier路径的“同步”图标版本,但是形状图层的性能成本要低得多 - 转换比位图的。不幸的是,你不能确定旋转是性能问题的来源 - 它很可能是造成大部分延迟的掩盖,在这种情况下你将完成所有的矢量化以获得很少的好处。

I think the best solution is to use UIImage's animation capabilities: create a filmstrip of every frame of the icon-rotation animation and simply display that animated UIImage in your tab bar. It's not the most elegant solution, but a number of animations across the system—the Mail and Notes trash-can “delete” icon and the various forms of activity indicator, for instance—are implemented the same way.

我认为最好的解决方案是使用UIImage的动画功能:创建图标旋转动画的每一帧的幻灯片,并在标签栏中显示动画UIImage。这不是最优雅的解决方案,但是系统中的一些动画 - 邮件和便笺垃圾 - 可以“删除”图标和各种形式的活动指示器 - 例如 - 以相同的方式实现。

#1


15  

Brent,

布伦特,

Why do you need to use a layer mask? Can't you just convert your mask layer to a sublayer? You would just need to make sure your image had the proper alpha and you would use it's CGImageRef as that layer's contents.

为什么需要使用图层蒙版?你不能只是将你的掩码层转换为子层吗?您只需要确保您的图像具有正确的alpha,并且您将使用它的CGImageRef作为该图层的内容。

One other thing. I haven't figured out why yet, but I've also noticed performance issues when I apply shouldRasterize on each layer instead of just the top layer. You might see if removing the reference to setShouldRasterize:YES in your mask layer helps at all.

还有一件事。我还没弄清楚原因,但是当我在每一层上应用shouldRasterize而不仅仅是顶层时,我也注意到了性能问题。您可能会看到在掩码图层中删除对setShouldRasterize:YES的引用是否有帮助。

#2


7  

One approach would be to create a CAShapeLayer to use as your mask—you'd have a fair bit of work cut out for you making a version of your “sync” icon with Bézier paths, but shape layers incur a much lower performance cost per-transformation than bitmap ones. Unfortunately, you can't be sure that the rotation is where the performance issue is coming from—it could very well be the masking causing most of the lag, in which case you'll have done all that vectorizing for little benefit.

一种方法是创建一个CAShapeLayer作为你的面具 - 你需要做一些工作来制作一个带有Bézier路径的“同步”图标版本,但是形状图层的性能成本要低得多 - 转换比位图的。不幸的是,你不能确定旋转是性能问题的来源 - 它很可能是造成大部分延迟的掩盖,在这种情况下你将完成所有的矢量化以获得很少的好处。

I think the best solution is to use UIImage's animation capabilities: create a filmstrip of every frame of the icon-rotation animation and simply display that animated UIImage in your tab bar. It's not the most elegant solution, but a number of animations across the system—the Mail and Notes trash-can “delete” icon and the various forms of activity indicator, for instance—are implemented the same way.

我认为最好的解决方案是使用UIImage的动画功能:创建图标旋转动画的每一帧的幻灯片,并在标签栏中显示动画UIImage。这不是最优雅的解决方案,但是系统中的一些动画 - 邮件和便笺垃圾 - 可以“删除”图标和各种形式的活动指示器 - 例如 - 以相同的方式实现。