好的,我根据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);
(holePath
是UIBezierPath
的实例。)
到目前为止一切顺利。下一步是动画。为了做到这一点(我在这里也发现了这种技术)我做了如下方法
-(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我想我的问题归结为:有没有更简单的方法来创建这个有一个洞的图层,它会重绘得更快?我的预感是,如果我可以简化我正在使用的路径,绘制路径会更轻。或者可能掩盖?请帮忙!
答案 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)
在您的示例中,您使用的是矩形孔。如果使用四个矩形图层,则实现此类动画可能会更有效(请参阅附图)。要为孔矩形位置设置动画,您必须为蓝色图层的宽度和红色图层的高度设置动画。 (如果孔矩形可以改变宽度,红色图层的宽度也必须是动画的)
如果您需要一个非矩形孔,您可以在其中间放置另一个图层,其中有一个洞并且仅更改该图层的位置(请参阅第二个图像)。调整此非矩形孔的大小将导致仅重新创建中间层的内容,因此它应该比原始情况下的速度快一点,如果我理解正确,此层的大小与屏幕的大小相同。
答案 3 :(得分:-1)
我发布了一个anwser,因为由于声誉,我无法对您的问题发表评论。
我的问题是这是否必须以编程方式创建?如果它只是创建一个关注区域,你可以使用另一种方法。
为什么不使用带有透明孔的黑色png?那么你在动画过程中没有性能问题,如果你选择原来的孔尺寸,你甚至可以调整它的大小。图像必须足够大,以便它可以覆盖视图的每个部分,而不依赖于孔的当前位置。孔外的部分还可以包括透明度,从而在注意区域外产生阴影效果。