UIBezierPath 减去路径
通过使用 [UIBezierPath bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:]
,我可以创建一个圆角视图,如下所示:
我如何从这条路径中减去另一条路径(或其他方式),以创建如下路径:
有什么办法我可以做这样的事情吗? 伪代码:
UIBezierPath *bigMaskPath = [UIBezierPath bezierPathWithRoundedRect:bigView.bounds
byRoundingCorners:(UIRectCornerTopLeft|UIRectCornerTopRight)
cornerRadii:CGSizeMake(18, 18)];
UIBezierPath *smallMaskPath = [UIBezierPath bezierPathWithRoundedRect:smalLView.bounds
byRoundingCorners:(UIRectCornerTopLeft|UIRectCornerTopRight)
cornerRadii:CGSizeMake(18, 18)];
UIBezierPath *finalPath = [UIBezierPath pathBySubtractingPath:smallMaskPath fromPath:bigMaskPath];
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
实际上,对于大多数情况,有一种更简单的方法,例如在 Swift 中:
这有效,因为默认填充规则是 非零缠绕规则。
Actually there is a much simpler way for most cases, example in Swift:
This works because the default fill rule is the non-zero winding rule.
如果你想抚摸减法的路径,你就得靠你自己了。 Apple 不提供返回(或仅描画)一条路径与另一条路径相减的 API。
如果您只想填充减去的路径(如示例图像中所示),您可以使用剪切路径来完成。不过,你必须使用一个技巧。当您向剪切路径添加路径时,新的剪切路径是旧剪切路径与添加路径的交集。因此,如果您只是将
smallMaskPath
添加到剪切路径,您最终将只填充smallMaskPath
内的区域,这与您想要的相反。您需要做的是将现有剪切路径与
smallMaskPath
的逆相交。幸运的是,您可以使用奇偶缠绕规则轻松地做到这一点。您可以在 Quartz 2D 编程指南。基本思想是,我们创建一个包含两个子路径的复合路径:您的
smallMaskPath
和一个完全包围您的smallMaskPath
以及您可能想要填充的所有其他像素的巨大矩形。由于奇偶规则,smallMaskPath
内部的每个像素将被视为复合路径外部,而smallMaskPath
外部的每个像素将被视为复合路径内部。复合路径。那么让我们创建这个复合路径。我们将从巨大的矩形开始。并且没有比无限矩形更大的矩形:
现在我们通过添加
smallMaskPath
使其成为复合路径:接下来我们设置路径以使用奇偶规则:
在我们剪辑之前这条路径,我们应该保存图形状态,以便完成后可以撤消对剪切路径的更改:
现在我们可以修改剪切路径:
并且我们可以填充
bigMaskPath
:最后我们恢复图形状态,撤消更改到剪切路径:
以下是代码,以防您想要复制/粘贴它:
If you want to stroke the subtracted path, you are on your own. Apple doesn't provide an API that returns (or just strokes) the subtraction of one path from another.
If you just want to fill the subtracted path (as in your example image), you can do it using the clipping path. You have to use a trick, though. When you add a path to the clipping path, the new clipping path is the intersection of the old clipping path and the added path. So if you just add
smallMaskPath
to the clipping path, you will end up filling only the region insidesmallMaskPath
, which is the opposite of what you want.What you need to do is intersect the existing clipping path with the inverse of
smallMaskPath
. Fortunately, you can do that pretty easily using the even-odd winding rule. You can read about the even-odd rule in the Quartz 2D Programming Guide.The basic idea is that we create a compound path with two subpaths: your
smallMaskPath
and a huge rectangle that completely encloses yoursmallMaskPath
and every other pixel you might want to fill. Because of the even-odd rule, every pixel inside ofsmallMaskPath
will be treated as outside of the compound path, and every pixel outside ofsmallMaskPath
will be treated as inside of the compound path.So let's create this compound path. We'll start with the huge rectangle. And there is no rectangle more huge than the infinite rectangle:
Now we make it into a compound path by adding
smallMaskPath
to it:Next we set the path to use the even-odd rule:
Before we clip to this path, we should save the graphics state so that we can undo the change to the clipping path when we're done:
Now we can modify the clipping path:
and we can fill
bigMaskPath
:Finally we restore the graphics state, undoing the change to the clipping path:
Here's the code all together in case you want to copy/paste it:
这样就可以了,根据需要调整大小:
获得所需效果的另一种非常简单的方法是绘制外部圆角矩形,更改颜色,然后在其上绘制内部圆角矩形。
This should do it, adjust sizes as you like:
Another really simple way to get the effect you're after is to just draw the outer roundrect, change colors, and draw the inner one over it.
使用 @Patrick Pijnappel 答案,您可以准备测试游乐场以进行快速测试
Using @Patrick Pijnappel answer, you can prepare test playground for quick testing
2019 - 太容易了
我对其他答案感到惊讶,因为这非常容易做到。可能有一些我不明白的要求。但是:
无论您是否进行切割,这都可以完美地工作。
没有问题,即使它们确实重叠:
和
(这是制作“凹口”或“缩进”的好方法。)
该技术是
(在示例中,我实际上是在使用它作为 UIView 上的掩码...)
为了节省任何人输入漏洞代码...
注意如果您不确定两条路径的运行方式,当然只需尝试每个
和
直到它起作用。
类似的有用提示:
制作发光盒:
https://stackoverflow.com/a/59092828/294884
2019 - too easy
I'm surprised at the other answers, because this is extremely easy to do. Could be there's some requirements I don't understand. But:
This does work perfectly whether or not you have cutting.
There is no problem, even if they do overlap:
and
(This is a great way to make a "notch" or "indentation".)
The technique is
(In the example, I'm literally using it as a mask on a UIView...)
To save anyone typing the hole code ...
Note If you're not sure which way the two paths are running, of course just try each of
and
until it works.
Similar useful tip:
Make a glow box:
https://stackoverflow.com/a/59092828/294884
我一直在用头撞墙,试图找出如何使用多个重叠的 CGPath 来做到这一点。如果您重叠多次,它会重新填充上面提供的解决方案。事实证明,真正实现具有多个重叠路径的“减去”效果的方法是将上下文的混合模式设置为清除。
CGContextSetBlendMode(ctx, kCGBlendModeClear);
I've been beating my head against the wall trying to figure out how to do this with multiple overlapping CGPaths. If you overlap more than once, it refills with the solutions provided above. It turns out the way to truly attain a 'subtract' effect with multiple overlapping paths is to just set the context's blend mode to clear.
CGContextSetBlendMode(ctx, kCGBlendModeClear);
Patrick 通过使用非零缠绕规则为用户 NSResponder 的答案提供了改进/替代方案。这是 Swift 的完整实现,适合任何正在寻找扩展答案的人。
这是要点。
您可以将其放入情节提要中以查看和使用输出。
Patrick provided an improvement/alternative to user NSResponder's answer by using the non-zero winding rule. Here is a full implementation in Swift for anyone that is looking for an expanded answer.
Here's a gist of this.
You can put this into a Storyboard to see and play around with the output.
这是视图上的两个透明洞:
Here is two transparent hole on View:
我没有使用减法,而是通过使用 CGPathAddLineToPoint 和 CGPathAddArcToPoint 来创建 CGPath 来解决。这也可以通过具有类似代码的
UIBiezerPath
来完成。Instead of subtracting, I was able to solve by creating a
CGPath
usingCGPathAddLineToPoint
andCGPathAddArcToPoint
. This can also be done with aUIBiezerPath
with similar code.