Core Animation CALayer遮罩动画表现
Posted
技术标签:
【中文标题】Core Animation CALayer遮罩动画表现【英文标题】:Core Animation CALayer mask animation performance 【发布时间】:2010-12-23 17:12:26 【问题描述】:我们想在我们的 iPhone 应用程序中使用 UITabBar,但有一个例外:我们有一个“同步”按钮,我想在同步操作发生时旋转它。
不幸的是,这意味着必须创建一个自定义标签栏,但这既不存在也不存在:我使用 Core Animation 实现的动画看起来很棒。问题是,在制作动画时,它会对屏幕上使用动画的其他所有操作的性能产生不利影响:UITableView 滚动、MKMapView 平移和图钉掉落等。我的测试设备是 iPhone 4。
问题似乎是我如何实现标签栏 - 我想实现与 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];
(注意:在上面的屏幕截图中,您可以看到我还添加了一个阴影层,但为了简单起见,我从代码中删除了它。我在动画时从同步图标中删除了阴影层,所以它应该不相关。)
要制作动画,我只需旋转遮罩层:
- (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
所以这一切都很好,除了性能。您可以看到我已经尝试过 Google 上出现的关于栅格化图层/使其不透明的提示 - 没有帮助。
我想我已经确定蒙版层是罪魁祸首。当我取出遮罩层并仅旋转 fgLayer
而不是它的遮罩时,性能非常好,尽管这肯定不是我想要的效果:
如果我旋转 fgLayer
而不是蒙版同时应用蒙版,性能也和以前一样差。
因此,如果动画的每一帧都必须重新合成蒙版会减慢速度,我是否可以使用其他技术来实现具有更好性能的类似效果?使用路径而不是蒙版层的图像?还是我必须降级到 OpenGL 或其他东西才能获得良好的性能?
更新:进一步强化了蒙版是减速的想法,我的同事建议尝试旋转一个仅包含图像作为内容的 CALayer——与我上面的示例类似,没有蒙版- 那样表现也很好。所以我真的只能做这样的纯色(没有渐变),但这可能是一个很好的临时解决方案。不过,我仍然希望能够旋转具有良好性能的面具,所以欢迎提出建议:)
【问题讨论】:
【参考方案1】:布伦特,
为什么需要使用图层蒙版?你不能把你的遮罩层转换成一个子层吗?您只需要确保您的图像具有正确的 alpha 并且您将使用它的 CGImageRef 作为该层的内容。
另一件事。我还没有弄清楚为什么,但是当我在每一层而不是顶层应用 shouldRasterize 时,我也注意到了性能问题。您可能会看到在遮罩层中删除对 setShouldRasterize:YES 的引用是否有帮助。
【讨论】:
您回答的第二部分是解决方案 - 我应该在顶层而不是子层上设置shouldRasterize
。这似乎使性能与没有面具一样好。我“需要”蒙版的原因是我可以用漂亮的渐变“填充”简单的 PNG 图标以增加视觉趣味。默认的 UITabBar 会这样做,所以我想做同样的事情(但使用黄色)。
酷,布伦特。很高兴就这么简单。如果您对问题的为什么有任何见解,我很想知道。 ;-)
我也很想知道为什么。此外,我很想知道如何光栅化顶层实际上可以提高蒙版动画的性能。它仍然在做我想要的 - 支持渐变图像保持静止,而蒙版正在旋转。它实际上是在动画开始之前为每一帧预先计算位图吗?好像魔法一样。在这种情况下,我喜欢魔法。 :)
我也想知道这里发生了什么。假设我正确阅读了您的评论并且您只是在包含视图的图层上设置 shouldRasterize
,也许 CA 采用 shouldRasterize
提示并将每个子层的屏蔽操作合并到一个操作中,使其无需为每个子层交换缓冲区蒙版层。您是否尝试在这两种情况下都打开 Instruments 中的“Color Offscreen”选项以查看 CA 正在制作多少渲染通道?如果我有时间,我会测试它,但如果你打败了我,请告诉我。我想揭开这个黑魔法的神秘面纱。 :)
我有同样的经历,我认为 shouldRasterize 应该改进动画,但它却滞后了。我做了仪器,整个图层(带有 shouldRasterize 的那个)是导致问题的屏幕外渲染。 PS。我知道这是一个旧线程。【参考方案2】:
一种方法是创建一个CAShapeLayer
用作您的蒙版——您需要做很多工作来制作一个带有贝塞尔路径的“同步”图标版本,但形状图层会导致每次转换的性能成本比位图低得多。不幸的是,您不能确定旋转是性能问题的根源——它很可能是导致大部分延迟的掩蔽,在这种情况下,您将完成所有这些矢量化,但收效甚微。
我认为最好的解决方案是使用UIImage
的动画功能:为图标旋转动画的每一帧创建幻灯片,然后在标签栏中简单地显示动画UIImage
。这不是最优雅的解决方案,但系统中的许多动画(例如,邮件和便笺垃圾桶“删除”图标和各种形式的活动指示器)都是以相同的方式实现的。
【讨论】:
感谢您的回答 - 使用动画幻灯片绝对是我正在考虑的事情,但听起来像很多工作 + 文件空间。幸运的是,superlayer 上的shouldRasterize
似乎达到了我预期的性能。
从答案看来,“set shouldRasterize to NO”解决了你的问题。以上是关于Core Animation CALayer遮罩动画表现的主要内容,如果未能解决你的问题,请参考以下文章
在 Core Animation 中为圆形箭头遮罩的长度设置动画