2

好的,我根据 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。)

到目前为止,一切都很好。下一步是动画。为了做到这一点(我也在SO上找到了这种技术)我做了一个方法如下

-(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:in的结果actionForKey:。现在,我得到了一个不错的“洞层”,它具有holeRect可使用隐式 CAAnimations 进行动画处理的属性!不幸的是,它非常不稳定。我每秒得到 2 或 3 帧。我以为可能是背景的问题,并尝试将其替换为快照,但没有骰子。然后,我使用 Instruments 进行分析,发现这里的 HUGE hog 是对CGContextDrawPath().

tl;博士我想我的问题归结为:有没有一种更简单的方法来创建这个带有一个洞的图层,这样可以更快地重绘?我的直觉是,如果我可以简化我正在使用的路径,那么绘制路径会更轻松。或者可能掩盖?请帮忙!

4

4 回答 4

3

好的,我尝试了 phix23 的建议,它完全成功了!起初,我是子类化CALayer并添加 aCAShapeLayer作为子层,但我无法让它正常工作,此时我已经很累了,所以我放弃了,只是将我的子类完全替换为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不是隐含的动画。我想这是有道理的。

于 2012-11-05T06:45:21.680 回答
2

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

我建议创建一个中间有孔的图层,并确保该图层足够大以覆盖整个屏幕,无论孔位于何处。然后动画应该为图层的“位置”属性设置动画。虽然拥有如此庞大的层(其中只有约 30% 一次会被使用)似乎很浪费,但与在每一帧上重绘相比,它的浪费要少得多。如果你想维护你的“holeRect”界面,你可以,但是具有“holeRect”属性的层应该包含一个或多个以我描述的方式动画的子层(在 layoutSubviews 中,而不是 drawInContext:)。

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

于 2012-11-05T03:32:35.767 回答
0

在您的示例中,您使用了一个矩形孔。如果您使用四个矩形层(见附图),实现这样的动画可能会更有效。要为孔矩形位置设置动画,您必须为蓝色层的宽度和红色层的高度设置动画。(如果孔矩形可以改变宽度,红色层的宽度也必须进行动画处理)

如果您需要一个非矩形孔,您可以在其中放置另一层,其中有孔并仅更改该层的位置(参见第二张图片)。调整这个非矩形孔的大小将导致只重新创建中间层的内容,所以如果我理解正确的话,它应该比原来的情况要快一点,在原来的情况下,这个层是屏幕大小。

多层方法 多层方法,非矩形孔

于 2012-11-05T05:00:31.587 回答
-1

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

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

为什么不直接使用带有透明孔的黑色 png 呢?那么你在动画过程中就不会出现性能问题,并且在某种程度上,如果你选择好原始孔尺寸,你甚至可以调整它的大小。图像必须足够大,以便它独立于孔的当前位置覆盖视图的每个部分。孔外的部分还可以包括透明度,从而在注意力区域之外产生阴影效果。

于 2012-10-31T08:22:43.347 回答