通过 CGMutablePathRef 为 drawRect 中绘制的框设置动画

Posted

技术标签:

【中文标题】通过 CGMutablePathRef 为 drawRect 中绘制的框设置动画【英文标题】:Animating a box drawn in drawRect via CGMutablePathRef 【发布时间】:2013-04-08 20:43:14 【问题描述】:

我的目标是在我的 UIView 上画一个空的圆形框,它应该显示为一个洞,以显示下面的内容。 我通过重写 drawRect 方法来做到这一点,如下所示。 这使我能够创建我的 holeRect 属性(它是一个 CGRect)大小的圆角矩形,将内部阴影放入其中并将其放置在我的视图上,清除(使用 clearColor 和 CGContextSetBlendMode(context, kCGBlendModeSourceOut) ) 这个框所覆盖的区域并揭示了我的观点背后的内容(我从this 问题中获取了一些代码并对其进行了修改)。 现在我的问题是我必须用动画移动和调整这个矩形的大小,我找不到这样做的方法,也许我选择了错误的方法来做到这一点,但我在绘画方面不是那么专家所以任何提示将不胜感激。

- (void)drawRect:(CGRect)rect




//I set the frame of my "holey" rect
CGRect bounds =  self.holeRect;
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat radius = 20;

//fill my whole view with gray
CGContextSetFillColorWithColor( context, [UIColor colorWithRed:.5 green:.5 blue:.5 alpha:.8].CGColor );
CGContextFillRect( context, rect );

// Create the "visible" path, which will be the shape that gets the inner shadow
// In this case it's just a rounded rect, but could be as complex as your want
CGMutablePathRef visiblePath = CGPathCreateMutable();
CGRect innerRect = CGRectInset(bounds, radius, radius);
CGPathMoveToPoint(visiblePath, NULL, innerRect.origin.x, bounds.origin.y);
CGPathAddLineToPoint(visiblePath, NULL, innerRect.origin.x + innerRect.size.width, bounds.origin.y);
CGPathAddArcToPoint(visiblePath, NULL, bounds.origin.x + bounds.size.width, bounds.origin.y, bounds.origin.x + bounds.size.width, innerRect.origin.y, radius);
CGPathAddLineToPoint(visiblePath, NULL, bounds.origin.x + bounds.size.width, innerRect.origin.y + innerRect.size.height);
CGPathAddArcToPoint(visiblePath, NULL,  bounds.origin.x + bounds.size.width, bounds.origin.y + bounds.size.height, innerRect.origin.x + innerRect.size.width, bounds.origin.y + bounds.size.height, radius);
CGPathAddLineToPoint(visiblePath, NULL, innerRect.origin.x, bounds.origin.y + bounds.size.height);
CGPathAddArcToPoint(visiblePath, NULL,  bounds.origin.x, bounds.origin.y + bounds.size.height, bounds.origin.x, innerRect.origin.y + innerRect.size.height, radius);
CGPathAddLineToPoint(visiblePath, NULL, bounds.origin.x, innerRect.origin.y);
CGPathAddArcToPoint(visiblePath, NULL,  bounds.origin.x, bounds.origin.y, innerRect.origin.x, bounds.origin.y, radius);
CGPathCloseSubpath(visiblePath);

// Fill this path
UIColor *aColor = [UIColor clearColor];
[aColor setFill];
CGContextAddPath(context, visiblePath);
CGContextSetBlendMode(context, kCGBlendModeSourceOut);
CGContextFillPath(context);


// Now create a larger rectangle, which we're going to subtract the visible path from
// and apply a shadow
CGMutablePathRef path = CGPathCreateMutable();
//(when drawing the shadow for a path whichs bounding box is not known pass "CGPathGetPathBoundingBox(visiblePath)" instead of "bounds" in the following line:)
//-42 cuould just be any offset > 0
CGPathAddRect(path, NULL, CGRectInset(bounds, -42, -42));

// Add the visible path (so that it gets subtracted for the shadow)
CGPathAddPath(path, NULL, visiblePath);
CGPathCloseSubpath(path);

// Add the visible paths as the clipping path to the context
CGContextAddPath(context, visiblePath);
CGContextClip(context);


// Now setup the shadow properties on the context
aColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.5f];
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake(0.0f, 1.0f), 5.0f, [aColor CGColor]);

// Now fill the rectangle, so the shadow gets drawn
[aColor setFill];   
CGContextSaveGState(context);   
CGContextAddPath(context, path);
CGContextEOFillPath(context);

// Release the paths
//CGPathRelease(path);
CGPathRelease(visiblePath);


【问题讨论】:

【参考方案1】:

看看我对一个关于在 UIImageView 中打孔的问题给出的答案:

https://***.com/a/8632731/341994

请注意,它的工作方式是 UIImageView 的父视图层有一个子层来进行遮罩,从而打孔。这意味着要为孔设置动画,您所要做的就是为该子层的移动设置动画。正如我的书中所述,Core Animation 很简单:

http://www.apeth.com/iosBook/ch17.html#_using_a_cabasicanimation

换句话说,如果您可以将您正在做的所有事情都封装为一个层,那么通过移动该层对其进行动画处理就很简单了。

【讨论】:

感谢您的回答,但这对我没有帮助,我正在阅读有关 Quartz 和 CA 的文档,但同时我似乎无法找到一种方法来制作类似于您的东西教程与我的代码,我没有使用任何类型的 UIImageView 等等。我需要使用我手动绘制的框并以某种方式对其进行动画处理。 我其他代码中的图像视图无关紧要。遮罩层可以在其后面的whatever 上打一个洞。因此,您只需为您的绘图设置动画,以及打孔的面具。 您知道为圆角矩形绘图制作动画很容易吗?如果没有,请先弄清楚,然后再担心打孔。也许您可以修改您的问题来描述您正在考虑的动画。 我所需要的只是在我的视图中调整我在上面显示的 drawRect 方法中绘制的图形的大小和移动(也许通过 CGRrect 指定新的大小和位置)。实际上我不知道如何为我的圆角矩形绘图设置动画,我能够使用 UIView 的方法 beginAnimations:context: 为事物设置动画,但从未通过之前的核心动画。现在我正在研究 Core Animation 和 CALayer 背后的内容,同时非常感谢任何可以加快我速度的帮助:)。 我已经向您指出了我书中的动画章节。在我看来,这是最好的解释!如果(我怀疑)您要通过绘制子图层来完成所有这些操作,那么您可能还想阅读前面的图层章节。【参考方案2】:

按照您已有的设置,您要做的是将切口的位置存储在视图类(实现此drawRect 方法的类)的实例变量中,并使用 NSTimer 定期更新位置。例如,在您的视图代码中,您可以像这样创建一个预定的计时器:

[NSTimer scheduledTimerWithTimeInterval:1.0/30.0 target:self selector:@selector(updateCutoutPosition) userInfo:nil repeats:YES];

然后实现更新方法:

- (void)updateCutoutPosition 
    // Do whatever you need to do to update the position of the cutout.
    [self setNeedsDisplay]; // Will cause drawRect to be called again soon.

【讨论】:

这个解决方案对 CPU 来说是不是太重了? 如果您打算在 drawRect 方法中绘制动画,那么您将拥有相对较高的 CPU 使用率。您是否可以避免这种低效率取决于您需要对切口做什么。它的形状在每个动画帧上都会改变吗?如果是这样,那么您将需要在此drawRect 方法或遮罩层的drawInContext 方法中绘制该形状。如果切口形状经常变化,只是位置变化,那么使用遮罩层是更好的方法。见***.com/questions/15609170/… 我在之前的评论中用完了空间,但是使用蒙版方法的想法是您将绘制蒙版层的形状一次(或不经常绘制),然后将蒙版层的框架设置为设置切口的位置。如果您只是动画帧,您可以使用[UIView animateWithDuration:… 方法,这将比重复触发drawRect 更有效。 问题是我还需要调整孔的大小,而不仅仅是移动它,这样做会迫使我重新绘制孔。 您也可以尝试通过使蒙版图层的框架更大或对蒙版图层应用缩放变换来更改大小,但这可能会使切口的边缘看起来模糊。毕竟你可能会被drawRect 卡住。

以上是关于通过 CGMutablePathRef 为 drawRect 中绘制的框设置动画的主要内容,如果未能解决你的问题,请参考以下文章

如何移动 CGMutablePathRef?

如何在 -dealloc 中正确释放 CGMutablePathRef?

释放 CGMutablePathRef 是有问题的

在我的自定义 UIView 中,设置子视图的 backgroundColor 会导致它覆盖我的 drawRect 绘图(CGContextRef、CGMutablePathRef 等)。怎么修?

CGContextRef&CGMutablePathRef&UIBezierPath简单学习

美国薪资最高的技术技能:GolangKafkaDynamoDBRedshiftCassandra