用一个圆形的切口遮盖UIView

18

我创建了一个带有半透明黑色背景的 UIView ,以覆盖主应用程序的视图。我想要实现的效果是在该 UIView 中某个位置创建一个剪切圆形图形,使半透明黑色背景不可见,从而产生整个图像上除了圆形区域以外为黑色的遮罩效果(这是为了突出显示该区域内的一个按钮)。

我已经使用 CAShapeLayer 创建了圆形蒙版,但这产生了相反的效果(即视图的其余部分是清晰的,在圆形内部是半透明黑色的)。我希望的是圆形外的所有内容都保持半透明黑色,然后内部的内容是清晰的。这是我使用的代码,请问如何使它以相反的方式工作?我的 blackMask 是半透明黑色视图,我的 buttonCircle 是我想要保持清晰的圆形。

也许我需要以某种方式反转路径?

CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
CGRect maskRect = buttonCircle.frame;
CGPathRef path = CGPathCreateWithEllipseInRect(maskRect, NULL);
maskLayer.path = path;
CGPathRelease(path);
blackMask.layer.mask = maskLayer;

编辑:现在正在尝试这个,但是使用它时根本看不到任何掩模:

UIView *circularMaskView = [[UIView alloc] initWithFrame:buttonCircle.frame];
circularMaskView.backgroundColor = [UIColor greenColor];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];

CGPathRef path = CGPathCreateWithEllipseInRect(buttonCircle.frame, NULL);
maskLayer.path = path;
CGPathRelease(path);

circularMaskView.layer.mask = maskLayer;

[blackMask addSubview:circularMaskView];

尝试在按钮的后面添加一个额外的视图(相同的框架),并将其作为子视图添加到黑色遮罩上。 - santhu
这样不行,按钮本身不是圆形的。我只想在它周围画一个圆。 - Luke
我是说创建一个新视图,将其背景颜色设置为某个值(非透明)。将此新视图的框架设置为buttonCircle.frame。将蒙版应用于此新视图(路径=buttonCircle.bounds),并将其作为子视图添加到blackMask中。 - santhu
1
创建一个类似于这个的反向路径? - David Rönnqvist
1
这似乎不能与 [UIColor clearColor] 一起使用。我可以制作一个绿色的遮罩或者其他颜色,位置等都是正确的,但如果我将颜色设置为透明,它就不起作用了。我已经检查了新视图上的 opaque 属性。 - Luke
显示剩余5条评论
3个回答

18

所以,我做了什么呢?我创建了一个名为BlackoutView的自定义UIView子类,如下所示:

BlackoutView.h

#import <UIKit/UIKit.h>

@interface BlackoutView : UIView

@property (nonatomic, retain) UIColor *fillColor;
@property (nonatomic, retain) NSArray *framesToCutOut;

@end

BlackoutView.m

#import "BlackoutView.h"

@implementation BlackoutView

- (void)drawRect:(CGRect)rect
{
    [self.fillColor setFill];
    UIRectFill(rect);

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetBlendMode(context, kCGBlendModeDestinationOut);

    for (NSValue *value in self.framesToCutOut) {
        CGRect pathRect = [value CGRectValue];
        UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:pathRect];
        [path fill];
    }

    CGContextSetBlendMode(context, kCGBlendModeNormal);
}

@end

我按照正常方式实例化它,并将属性设置为所需使用的蒙版颜色,以及要从蒙版中剪切出的帧:

[blackMask setFillColor:[UIColor colorWithWhite:0.0f alpha:0.8f]];
[blackMask setFramesToCutOut:@[[NSValue valueWithCGRect:buttonCircleA.frame],
                               [NSValue valueWithCGRect:buttonCircleB.frame]]];

如果我能裁剪除椭圆形以外的其他形状,那么这将得到改进,但对于我的目的来说它已经很好了,并且稍后可以轻松适应。希望这能帮助到其他人!


非常有帮助!你能解释一下为什么在BlackoutView.m中设置fillColor吗?我没有得到任何透明度,删除前两行代码后问题得到了解决。 - Oren
此外,在 drawRect:rect 的末尾是否需要添加 CGContextRelease(context); - Oren
1
我尝试了很多解决方案,但这个真的帮了我!非常感谢 :) - Josip B.
填充方法出现错误,导致背景颜色变成了纯黑色。已进行编辑以修正此问题。 - averydev
1
我刚刚使用标准的backgroundColor设置了背景颜色,并删除了fillColor属性和drawRect中的前两行。设置fillColor仍然导致纯黑色。我正在iOS8上工作,但我不确定这是否有任何关系。 - Lukas
即使我删除了 setFill 并且将 backgroundColor 设置为标准值,仍然会出现黑屏。 - Itai Spector

9

如果您使用任何黑色图像来掩盖

此句话暂无上下文,如果需要更准确的翻译,请提供更多的上下文信息。
-(void)maskWithImage:(UIImage*)maskImage toImageView:(UIImageView*)imgView{

    CALayer *aMaskLayer=[CALayer layer];
    aMaskLayer.contents=(id)maskImage.CGImage;

    imgView.layer.mask=aMaskLayer;


}

如果您有任何自定义的ShapeLayer需要进行蒙版处理

-(void)maskWithShapeLayer:(CALayer*)layer toImageView:(UIImageView*)imgView{
    //Layer should be filled with Black color to mask those part of Image
    imgView.layer.mask=layer;
}

如果您想使用圆形制作ImageView遮罩

-(void)maskWithCircle:(UIImageView*)imgView{

    CAShapeLayer *aCircle=[CAShapeLayer layer];
    aCircle.path=[UIBezierPath bezierPathWithRoundedRect:imgView.bounds cornerRadius:imgView.frame.size.height/2].CGPath; // Considering the ImageView is square in Shape

    aCircle.fillColor=[UIColor blackColor].CGColor;
    imgView.layer.mask=aCircle;

}

5

以下是我对原答案的用Swift进行的转换:

public class OverlayView: UIView {
let fillColor: UIColor = UIColor.grayColor()
public var framesToCutOut: Array<NSValue> = [NSValue]()

override public func drawRect(rect: CGRect) {
    super.drawRect(rect)

    fillColor.setFill()
    UIRectFill(rect)

    if let context: CGContextRef = UIGraphicsGetCurrentContext() {
        CGContextSetBlendMode(context, .DestinationOut)

        for value in framesToCutOut {
            let pathRect: CGRect = value.CGRectValue()
            let path: UIBezierPath = UIBezierPath(ovalInRect: pathRect)
            path.fill()
        }

        CGContextSetBlendMode(context, .Normal)
    }
}

2
如果您正在使用其他Swift代码调用此Swift代码,则无需将CGRect放入NSValue中 - 只需将frameToCutOut设置为[CGRect]数组即可。 - xaphod

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