在CALayer中平滑动画的洞?

时间:2012-10-26 03:45:26

标签: ios core-graphics calayer caanimation

好的,我根据SO上的各种帖子想出了如何做到这一点,而且效果很好。我正在制作一个叠加层,它基本上会掩盖整个窗口,除了一个小区域。这是为了吸引注意我的应用程序的特定区域。我正在使用moveToPoint:addLineToPoint:这样的大量调用(这是在我的CALayer子类'drawInContext:中):

....

// inner path (CW)
[holePath moveToPoint:CGPointMake(x, y)];
[holePath addLineToPoint:CGPointMake(x + w, y)];
[holePath addLineToPoint:CGPointMake(x + w, y + h)];
[holePath addLineToPoint:CGPointMake(x, y+h)];

// outer path (CCW)
[holePath moveToPoint:CGPointMake(xBounds, yBounds)];
[holePath addLineToPoint:CGPointMake(xBounds, yBounds + hBounds)];
[holePath addLineToPoint:CGPointMake(xBounds + wBounds, yBounds + hBounds)];
[holePath addLineToPoint:CGPointMake(xBounds + wBounds, yBounds)];

// put the path in the context
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, 0, 0);
CGContextAddPath(ctx, holePath.CGPath);
CGContextClosePath(ctx);

// set the color
CGContextSetFillColorWithColor(ctx, self.overlayColor.CGColor);

// draw the overlay
CGContextDrawPath(ctx, kCGPathFillStroke);

holePathUIBezierPath的实例。)

到目前为止一切顺利。下一步是动画。为了做到这一点(我在这里也发现了这种技术)我做了如下方法

-(CABasicAnimation *)makeAnimationForKey:(NSString *)key {
    CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:key];
    anim.fromValue = [[self presentationLayer] valueForKey:key];
    anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    anim.duration = 0.5;

    return anim;
}

并覆盖actionForKey:initWithLayer:needsDisplayForKey:(在makeAnimationForKey:中返回actionForKey:的结果。现在,我得到了一个不错的“洞层”,它有一个使用隐式CAAnimations可动画的属性holeRect!不幸的是,它是超级波动。我得到的东西就像每秒2或3帧。我想也许问题是背景,并尝试用快照替换它但是没有骰子。然后,我用仪器进行了剖析,发现这里巨大的猪只是对CGContextDrawPath()的呼唤。

tl; dr我想我的问题归结为:有没有更简单的方法来创建这个有一个洞的图层,它会重绘得更快?我的预感是,如果我可以简化我正在使用的路径,绘制路径会更轻。或者可能掩盖?请帮忙!

4 个答案:

答案 0 :(得分:3)

好的,我尝试了phix23的建议,它完全成功了!起初,我正在继承CALayer并添加CAShapeLayer作为子图层,但我无法使其正常工作,此时我已经很累了,所以我放弃了,只是更换了我的子类完全使用CAShapeLayer!我在自己的方法中使用了上面的代码,返回UIBezierPath,并像这样动画:

UIBezierPath* oldPath = [self pathForHoleRect:self.holeRect];
UIBezierPath* newPath = [self pathForHoleRect:rectToHighlight];
self.holeRect = rectToHighlight;

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
animation.duration = 0.5;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.fromValue = (id)oldPath.CGPath;
animation.toValue = (id)newPath.CGPath;
[self.holeLayer addAnimation:animation forKey:@"animatePath"];
self.holeLayer.path = newPath.CGPath;

有趣的旁注 - path不是隐式可动画的。我想这是有道理的。

答案 1 :(得分:1)

重绘是昂贵的,而动画则不是。当您设置“holeRect”的值时,您正在指示整个图层重绘。 这不是典型性能优化的属性动画。如果图层是整个屏幕的大小,那么您实际上是在每个帧上重新创建图层的新版本。这是苹果为确保平滑动画所做的事情之一。你想尽可能地防止重绘。

我建议在中心创建一个带孔的图层,并确保图层足够大以覆盖整个屏幕,无论孔在哪里居中。然后动画应该为图层的“位置”属性设置动画。虽然这个庞大的层一次只能使用约30%看起来似乎很浪费,但是在每一帧上重绘都要浪费得多。如果你想维护你的“holeRect”接口,但是具有“holeRect”属性的图层应该包含一个子层或子层,这些子层或子层以我描述的方式动画(在layoutSubviews中,而不是drawInContext :)。

总之,请确保您为动画位置,不透明度,变换设置动画,因为它们是图层上最有效的动画,并且仅在必要时重绘。

答案 2 :(得分:0)

在您的示例中,您使用的是矩形孔。如果使用四个矩形图层,则实现此类动画可能会更有效(请参阅附图)。要为孔矩形位置设置动画,您必须为蓝色图层的宽度和红色图层的高度设置动画。 (如果孔矩形可以改变宽度,红色图层的宽度也必须是动画的)

如果您需要一个非矩形孔,您可以在其中间放置另一个图层,其中有一个洞并且仅更改该图层的位置(请参阅第二个图像)。调整此非矩形孔的大小将导致仅重新创建中间层的内容,因此它应该比原始情况下的速度快一点,如果我理解正确,此层的大小与屏幕的大小相同。

Multilayer approach Multilayer approach, non-rectangular hole

答案 3 :(得分:-1)

我发布了一个anwser,因为由于声誉,我无法对您的问题发表评论。

我的问题是这是否必须以编程方式创建?如果它只是创建一个关注区域,你可以使用另一种方法。

为什么不使用带有透明孔的黑色png?那么你在动画过程中没有性能问题,如果你选择原来的孔尺寸,你甚至可以调整它的大小。图像必须足够大,以便它可以覆盖视图的每个部分,而不依赖于孔的当前位置。孔外的部分还可以包括透明度,从而在注意区域外产生阴影效果。