【问题标题】:Invert CALayer mask that is based on a stroke (no fill)反转基于描边的 CALayer 蒙版(无填充)
【发布时间】:2016-12-08 07:40:23
【问题描述】:

尊敬的堆栈溢出社区,

我对 iOS (Swift) 中 CAShapeLayer 的 mask 属性有疑问。

我想要实现的是一个 橡皮擦,它通过遮罩来擦除图像层的一部分。当我尝试反转它时,问题就来了。

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

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

  1. 创建一个CAShapeLayer 来保存橡皮擦笔划的路径
  2. 设置图层的fill-colornil
  3. 设置stroke-colorline width
  4. 添加图层作为图像图层的蒙版

这可以正常工作,但它只会使图像的笔画内的部分可见。我想反转。我想到了一个黑白蒙版,但这不起作用,因为蒙版是通过 alpha 通道传递的。

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

【问题讨论】:

  • 您能否添加任何描述您想要实现的目标的照片?
  • 我只能为自己说话,但我有一条线描边的路径。并且想要剪掉圆圈(如屏幕截图所示)。我想剪掉行尾。 (我不想修饰路径本身)截图:d17oy1vhnax1f7.cloudfront.net/items/0b3A3D2H3B3v01221G1I/…
  • 热心提供帮助,但如果您能澄清一下,我将不胜感激 - 您是否正在寻找一种通过任何可能的方式创建橡皮擦的方法,或者以如下方式实现橡皮擦是否重要?你已经概述了吗?
  • 我需要一个能发挥上述作用的橡皮擦。橡皮擦本身不能有填充颜色。也许你可以有一个不可见的层,它有一个 fillColor 并且那个层遮住了这个层。不确定,会尝试。

标签: ios swift calayer mask


【解决方案1】:

您可以通过将透明颜色绘制到非透明图层来实现此目的。这可以通过使用另一种混合模式进行绘制来完成。不幸的是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

创建具有透明路径的层:

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

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

编辑:下面是 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)
        }
    }
}

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

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

【讨论】:

  • 非常感谢,这正是我想要的。效果很好
猜你喜欢
  • 2017-07-03
  • 2017-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多