drawRect中的iOS反转遮罩

hsgswve4  于 2023-02-10  发布在  iOS
关注(0)|答案(9)|浏览(143)

使用下面的代码,我成功地屏蔽了我的绘图的一部分,但它与我想要屏蔽的相反。这屏蔽了绘图的内部,而我想要屏蔽外部。有简单的方法来反转这个屏蔽吗?
下面的myPath是一个UIBezierPath

CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
CGMutablePathRef maskPath = CGPathCreateMutable();
CGPathAddPath(maskPath, nil, myPath.CGPath);
[maskLayer setPath:maskPath];
CGPathRelease(maskPath);
self.layer.mask = maskLayer;
rnmwe5a2

rnmwe5a21#

在形状层(maskLayer.fillRule = kCAFillRuleEvenOdd;)上使用奇偶填充,你可以添加一个覆盖整个帧的大矩形,然后添加你要遮罩的形状。这将有效地反转遮罩。

CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
CGMutablePathRef maskPath = CGPathCreateMutable();
CGPathAddRect(maskPath, NULL, someBigRectangle); // this line is new
CGPathAddPath(maskPath, nil, myPath.CGPath);
[maskLayer setPath:maskPath];
maskLayer.fillRule = kCAFillRuleEvenOdd;         // this line is new
CGPathRelease(maskPath);
self.layer.mask = maskLayer;
wgx48brx

wgx48brx2#

适用于Swift 3.0

func mask(viewToMask: UIView, maskRect: CGRect, invert: Bool = false) {
    let maskLayer = CAShapeLayer()
    let path = CGMutablePath()
    if (invert) {
        path.addRect(viewToMask.bounds)
    }
    path.addRect(maskRect)

    maskLayer.path = path
    if (invert) {
        maskLayer.fillRule = kCAFillRuleEvenOdd
    }

    // Set the mask of the view.
    viewToMask.layer.mask = maskLayer;
}
js4nwp54

js4nwp543#

基于这个公认的答案,下面是Swift中的另一个mashup,我将它变成了一个函数,并将invert设置为可选

class func mask(viewToMask: UIView, maskRect: CGRect, invert: Bool = false) {
    let maskLayer = CAShapeLayer()
    let path = CGPathCreateMutable()
    if (invert) {
        CGPathAddRect(path, nil, viewToMask.bounds)
    }
    CGPathAddRect(path, nil, maskRect)

    maskLayer.path = path
    if (invert) {
        maskLayer.fillRule = kCAFillRuleEvenOdd
    }

    // Set the mask of the view.
    viewToMask.layer.mask = maskLayer;
}
zazmityj

zazmityj4#

这是我的Swift 4.2解决方案,它允许转弯半径

extension UIView {

    func mask(withRect maskRect: CGRect, cornerRadius: CGFloat, inverse: Bool = false) {
        let maskLayer = CAShapeLayer()
        let path = CGMutablePath()
        if (inverse) {
            path.addPath(UIBezierPath(roundedRect: self.bounds, cornerRadius: cornerRadius).cgPath)
        }
        path.addPath(UIBezierPath(roundedRect: maskRect, cornerRadius: cornerRadius).cgPath)

        maskLayer.path = path
        if (inverse) {
            maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
        }

        self.layer.mask = maskLayer;
    }

}
biswetbf

biswetbf5#

雨燕5

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let red = UIView(frame: view.bounds)
        view.addSubview(red)
        view.backgroundColor = UIColor.cyan
        red.backgroundColor = UIColor.red
        red.mask(CGRect(x: 50, y: 50, width: 50, height: 50), invert: true)
    }
}

extension UIView{

    func mask(_ rect: CGRect, invert: Bool = false) {
        let maskLayer = CAShapeLayer()
        let path = CGMutablePath()
        if (invert) {
            path.addRect(bounds)
        }
        path.addRect(rect)
        maskLayer.path = path
        if (invert) {
            maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
        }
        // Set the mask of the view.
        layer.mask = maskLayer
    }
}

谢谢@arvidurs

cyvaqqii

cyvaqqii6#

适用于Swift 4.2

func mask(viewToMask: UIView, maskRect: CGRect, invert: Bool = false) {
    let maskLayer = CAShapeLayer()
    let path = CGMutablePath()
    if (invert) {
        path.addRect(viewToMask.bounds)
    }
    path.addRect(maskRect)

    maskLayer.path = path
    if (invert) {
        maskLayer.fillRule = .evenOdd
    }

    // Set the mask of the view.
    viewToMask.layer.mask = maskLayer;
}
vjrehmav

vjrehmav7#

为了反转蒙版,你可以这样做
1.这是交叉直肠的面具

let crossPath =  UIBezierPath(rect: cutout.insetBy(dx: 30, dy: -5))
          crossPath.append(UIBezierPath(rect: cutout.insetBy(dx: -5, dy: 30)))
            let crossMask = CAShapeLayer()
            crossMask.path = crossPath.cgPath
            crossMask.backgroundColor = UIColor.clear.cgColor
            crossMask.fillRule = .evenOdd

1.这里我在交叉矩形周围添加第三个矩形,所以使用。evenOdd,它占用的面积等于(新矩形-旧交叉矩形),换句话说,在交叉矩形的面积之外

let crossPath = UIBezierPath(rect: cutout.insetBy(dx: -5, dy: -5) )
    crossPath.append(UIBezierPath(rect: cutout.insetBy(dx: 30, dy: -5)))
    crossPath.append(UIBezierPath(rect: cutout.insetBy(dx: -5, dy: 30)))
    let crossMask = CAShapeLayer()
    crossMask.path = crossPath.cgPath
    crossMask.backgroundColor = UIColor.clear.cgColor
    crossMask.fillRule = .evenOdd

sxissh06

sxissh068#

有很多很好的答案,它们都使用奇偶规则来解决内部和外部表面。以下是Swift 5使用非零规则的方法:

extension UIView {
    func mask(_ rect: CGRect, cornerRadius: CGFloat, invert: Bool = false) {
        let maskLayer = CAShapeLayer()
        let path = CGMutablePath()
        
        if (invert) {
            path.move(to: .zero)
            path.addLine(to: CGPoint(x: 0, y: bounds.height))
            path.addLine(to: CGPoint(x: bounds.width, y: bounds.height))
            path.addLine(to: CGPoint(x: bounds.width, y: 0))
            path.addLine(to: .zero)
        }
        
        path.addPath(UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).cgPath)
        maskLayer.path = path
        layer.mask = maskLayer
    }
}

对于蒙版操作,这是相当直接的。我们画矩形,并使用它作为蒙版路径。
对于反向遮罩,我们使用将要被遮罩的视图的边界,然后逆时针绘制它,然后添加默认为顺时针绘制的UIBezierPath矩形,这样矩形内的任何点p都将有一个顺时针交点和一个逆时针交点,导致总缠绕数为零,使该点成为外部点。

pgpifvop

pgpifvop9#

适用于2023年。目前这里的所有答案似乎都是错误的,因为它们在布局更改时(或者-仅举一个例子-当视图的大小或形状动画时)不调整遮罩。

很简单...

    • 1.**有一个图层(也许只是一个颜色方块,一个图像,无论什么)
lazy var examp: CALayer = {
    let l = CALayer()
    l.background = UIColor.blue.cgColor
    l.mask = shapeAsMask
    layer.addSublayer(l)
    return l
}()

注意,examp已经设置了掩码。

    • 2.**无论何时你想要遮蔽,你当然需要一个遮蔽层
lazy var shapeAsMask: CAShapeLayer = {
    let s = CAShapeLayer()
    s.fillRule = .evenOdd
    layer.addSublayer(s)
    layer.mask = s
    return s
}()

请注意,填充规则是根据需要设置的。

    • 3.**现在用贝塞尔曲线做一个形状。让我们做一个圆:
override func layoutSubviews() {
    super.layoutSubviews()
    
    examp.frame = bounds

    let i = bounds.width * 0.30
    let thing = UIBezierPath(ovalIn: bounds.insetBy(dx: i, dy: i))
    ...
}

这是视图中间的一个小圆圈。

如果你想"拥有形状",那就

override func layoutSubviews() {
    super.layoutSubviews()
    
    fill.frame = bounds

    let i = bounds.width * 0.30
    let thing = UIBezierPath(ovalIn: bounds.insetBy(dx: i, dy: i))
    
    shapeAsMask.path = thing.cgPath
}

如果你想"拥有形状的反转",那就

override func layoutSubviews() {
    super.layoutSubviews()
    
    fill.frame = bounds

    let i = bounds.width * 0.30
    let thing = UIBezierPath(ovalIn: bounds.insetBy(dx: i, dy: i))

    let p = CGMutablePath()
    p.addRect(bounds)
    p.addPath(thing.cgPath)

    shapeAsMask.path = p
}

简而言之,这个的"负面"。

let thing = UIBezierPath( ... some path

是这样的

let neg = CGMutablePath()
neg.addRect(bounds)
neg.addPath(thing.cgPath)

原来如此。
别忘了...
任何时候你有一个遮罩(或者一个图层!)你必须把它设置在layoutSubviews中。(这就是为什么layoutSubviews被命名为layoutSubviews!)

相关问题