iPhone - 在UIView上绘制透明矩形以显示下面的视图

38

我目前有两个UIView:一个红色背景,另一个是蓝色的。蓝色视图是红色视图的子视图。我想做的是在蓝色视图上“切割”出矩形,从而可以显示红色视图。你如何实现这个功能?


对于一个即插即用的解决方案,请参见我在这个问题中的回答:https://dev59.com/M2zXa4cB1Zd3GeqPXMP-#31255084 - clozach
我最终在这里发布了我对这个问题的解决方案:https://dev59.com/M2zXa4cB1Zd3GeqPXMP-#32941652 - James Laurenstin
5个回答

76

你需要重写顶部视图的 drawRect 方法。例如,你可以创建一个继承自 UIViewHoleyView 类(可以通过在项目中添加新文件、选择 Objective-C 子类并将 "Subclass of" 设置为 UIView 来实现)。在 HoleyView 中,drawRect 方法应该类似于这样:

- (void)drawRect:(CGRect)rect {
    // Start by filling the area with the blue color
    [[UIColor blueColor] setFill];
    UIRectFill( rect );

    // Assume that there's an ivar somewhere called holeRect of type CGRect
    // We could just fill holeRect, but it's more efficient to only fill the
    // area we're being asked to draw.
    CGRect holeRectIntersection = CGRectIntersection( holeRect, rect );

    [[UIColor clearColor] setFill];
    UIRectFill( holeRectIntersection );
}

如果您正在使用Interface Builder,请确保将holey视图的类更改为HoleyView。您可以通过在Interface Builder中选择该视图并在检查器中选择"Identity"面板(在 "i" 图标的最右侧)来完成此操作。

您还必须使用以下代码片段设置顶部视图为非不透明状态,或者通过取消Interface Builder中视图属性中的Opaque复选框(在视图属性的View部分中找到),并将其背景颜色的不透明度设置为0%(在同一部分设置背景颜色)。

topView.opaque = NO;
topView.backgroundColor = [UIColor clearColor];
如果你想要绘制圆形,你需要使用 Core Graphics(也称为 Quartz 2D)。你可能需要阅读编程指南,可以在这里找到。
如果要绘制椭圆而不是矩形,你的 drawRect 函数看起来会像这样:
- (void)drawRect:(CGRect)rect {
    // Get the current graphics context
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetFillColorWithColor( context, [UIColor blueColor].CGColor );
    CGContextFillRect( context, rect );

    if( CGRectIntersectsRect( holeRect, rect ) )
    {
        CGContextSetFillColorWithColor( context, [UIColor clearColor].CGColor );
        CGContextFillEllipseInRect( context, holeRect );
    }
}

8
使用clearColor作为填充颜色的UIGraphicsFillRect函数不会擦除矩形的内容,而是在现有内容上绘制空白颜色。CGContextClearRect是唯一能够“擦除”CGContext某部分内容的函数(尽管通常不建议使用它--最好使用遮罩来控制之前的绘图命令)。 - rpetrich
快了……洞只画了一次(我的清晰视图是可拖动的),但随后调用:<错误>:CGContextSetCompositeOperation:无效上下文0x0 - Morkrom
我正在尝试绘制带有阴影的孔,但遇到了很大的困难... 如果有技巧或指针,将不胜感激... - livingtech
这个答案对我没用。我不得不像另一个答案建议的那样添加CGContextSetBlendMode(context, kCGBlendModeClear)。 - avance
@studyro,你需要将opaque设置为false,这一步是必需的。 - Dima Deplov
显示剩余7条评论

35

其他答案都有一定道理,但是使用清晰的颜色绘制,或者说在任何路径中"擦除"现有颜色,甚至使用-[UIBezierPath fill]或类似的便捷方法也是完全可能的。您需要做的就是根据您要实现的效果将上下文混合模式设置为适当的值,如下所示:

CGContextSetBlendMode(context, kCGBlendModeClear);
[[UIColor clearColor] set];
[myArbitraryPolygonPath fill];

你可以在CGContext参考文献中查看大约两打不同的选项。


你甚至不需要设置清晰的颜色。无论源颜色如何,清晰混合模式都会将预乘像素值设置为0。 - CIFilter
你能详细说明如何使用这个来解决我的问题吗:http://stackoverflow.com/questions/42066123/mask-all-views-below-a-uiview-except-background - damjandd
1
这很棒。在Swift中使用,调用UIBezierPath.fill(with blendMode: CGBlendMode, alpha: CGFloat)。 - Mathieson

18

要画椭圆而不是矩形,只需将 blendMode 设置为 clear:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetFillColorWithColor( context, [UIColor blackColor].CGColor );
    CGContextFillRect( context, rect );

    CGRect holeRectIntersection = CGRectIntersection( holeRect, rect );

    CGContextSetFillColorWithColor( context, [UIColor clearColor].CGColor );
    CGContextSetBlendMode(context, kCGBlendModeClear);

    CGContextFillEllipseInRect( context, holeRect );
}

最后一行应该包含 holeRectIntersection 而不是 holeRect. - iphonic

1

这些可能是愚蠢的方法,但我不会按照你描述的方式去做,而是让它看起来像你想要的样子。

(Jacques的答案突然出现了 - 对我来说看起来很好)

方法1: 在视图控制器中构建一个矩形列表,以平铺周围的“洞口”。随着您的孔数增加,平铺矩形的数量也会增加。

方法2: 反转您的思维。蓝色视图应该在后面,红色视图的部分放置在其上。您仍然可以看到一个红色视图,除了“孔”被蓝色视图遮盖之外,但实际上您正在从要公开的视图中复制区域,并将它们放在每个孔上方的遮罩上。如果您有一些模拟深度的效果,可以根据需要添加每个孔。

两种方法都不需要子类化或drawRect:。


0

一些答案相当陈旧。

2023年,最新技术包括...

 class CompImage: UIIImageView {

     override func common() {
         super.common()
         layer.compositingFilter = "multiplyBlendMode"
         
         print("Filters available to you ...\n",
          CIFilter.filterNames(inCategory: kCICategoryCompositeOperation))
     }
 }

并且

guard let ctx = UIGraphicsGetCurrentContext() else { return }
ctx.saveGState()
ctx.setStrokeColor(
..
ctx.setBlendMode(.multiply) // review the choices available
..

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接