反转基于笔划的CALayer蒙版(无填充)

Lar*_*sen 10 mask calayer ios swift

亲爱的堆栈溢出社区,

我有一个关于CAShapeLayeriOS(Swift)中的掩码属性的问题.

我想要实现的是一个擦除图像层部分的橡皮擦.当我试图颠倒它时,问题出现了.

我在反转路径时找到了一些好的答案,但这些仅在使用填充路径时才有用.我尝试做的是中风的路径,并使用倒置一个掩盖的图像.笔划的线宽应该在左右,30.0所以看起来像橡皮擦.

我尝试了不同的东西.我当前的版本如下所示:

  1. 创建一个CAShapeLayer包含橡皮擦笔划路径的文件
  2. fill-color图层设置为nil
  3. 设置stroke-colorline width
  4. 添加图层作为图像层的蒙版

这是工作正常,但它只是使图像可见的部分是行程内.我想颠倒过来.我想到了一个黑白面具,但这不起作用,因为面具是通过alpha通道传递的.

有谁知道如何解决这个问题?

cle*_*ens 6

您可以通过将透明颜色绘制到非不透明图层来实现此目的。这可以通过使用另一种混合模式进行绘制来完成。不幸的CAShapeLayer是不支持这一点。因此,您必须编写自己的形状图层类:

@interface ShapeLayer : CALayer

@property(nonatomic) CGPathRef path;
@property(nonatomic) CGColorRef fillColor;
@property(nonatomic) CGColorRef strokeColor;
@property(nonatomic) CGFloat lineWidth;

@end

@implementation ShapeLayer

@dynamic path;
@dynamic fillColor;
@dynamic strokeColor;
@dynamic lineWidth;

- (void)drawInContext:(CGContextRef)inContext {
    CGContextSetGrayFillColor(inContext, 0.0, 1.0);
    CGContextFillRect(inContext, self.bounds);
    CGContextSetBlendMode(inContext, kCGBlendModeSourceIn);
    if(self.strokeColor) {
        CGContextSetStrokeColorWithColor(inContext, self.strokeColor);
    }
    if(self.fillColor) {
        CGContextSetFillColorWithColor(inContext, self.fillColor);
    }
    CGContextSetLineWidth(inContext, self.lineWidth);
    CGContextAddPath(inContext, self.path);
    CGContextDrawPath(inContext, kCGPathFillStroke);
}

@end
Run Code Online (Sandbox Code Playgroud)

创建具有透明路径的图层:

ShapeLayer *theLayer = [ShapeLayer layer];

theLayer.path = ...;
theLayer.strokeColor = [UIColor clearColor].CGColor;
theLayer.fillColor = [UIColor colorWithWhite:0.8 alpha:0.5];
theLayer.lineWith = 3.0;
theLayer.opaque = NO; // Important, otherwise you will get a black rectangle
Run Code Online (Sandbox Code Playgroud)

我已经使用此代码在绿色背景前绘制了一个带有透明边框的半透明圆圈:

在此处输入图片说明

编辑:这是Swift中图层的相应代码:

public class ShapeLayer: CALayer {
    @NSManaged var path : CGPath?
    @NSManaged var fillColor : CGColor?
    @NSManaged var strokeColor : CGColor?
    @NSManaged var lineWidth : CGFloat

    override class func defaultValue(forKey inKey: String) -> Any? {
        return inKey == "lineWidth" ? 1.0 : super.defaultValue(forKey: inKey)
    }

    override class func needsDisplay(forKey inKey: String) -> Bool {
        return inKey == "path" || inKey == "fillColor" || inKey == "strokeColor" || inKey == "lineWidth" || super.needsDisplay(forKey: inKey)
    }

    override public func draw(in inContext: CGContext) {
        if let thePath = path {
            inContext.setFillColor(gray: 0.0, alpha: 1.0)
            inContext.fill(self.bounds)
            inContext.setBlendMode(.sourceIn)
            if let strokeColor = self.strokeColor {
                inContext.setStrokeColor(strokeColor)
            }
            if let fillColor = self.fillColor {
                inContext.setFillColor(fillColor)
            }
            inContext.setLineWidth(self.lineWidth)
            inContext.addPath(thePath)
            inContext.drawPath(using: .fillStroke)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:通过分别在 Swift 或Objective C 中@NSManaged实现needsDisplay(forKey inKey:),通过标记属性,您可以轻松地使属性具有动画效果needsDisplayForKey:。我已经相应地调整了 Swift 代码。

但即使你不需要动画,最好用 标记属性@NSManaged,因为 QuartzCore 制作层的副本并且也应该用它复制所有属性。@NSManaged在 Swift 中与@dynamic在 Objective C 中对应,因为它避免了创建属性实现。而是分别CALayer使用value(forKey:)和获取和设置属性值setValue(_:forKey:)