如何将多个蒙版应用于UIView

时间:2018-06-06 11:38:01

标签: ios swift uiview mask cashapelayer

我有一个关于如何将多个蒙版应用于已经有掩码的UIView的问题。

情况:
我有一个带有活动蒙版的视图,它在左上角创建了一个洞,这是一个模板UIView,可以在项目的任何地方重复使用。在项目的后期,我希望能够创建第二个洞,但这次是在右下角,这不需要创建一个全新的UIView。

问题:
当我应用底部面具时,它当然会取代第一个,从而移除顶部孔......有没有办法将它们组合在一起?那么将任何现有的面具与新的面具结合起来吗?

提前谢谢!

3 个答案:

答案 0 :(得分:2)

这是我在我的项目中用于在UIView中创建一个圆和一个矩形蒙版的代码,您可以用相同的弧代码替换UIBezierPath行:

func createCircleMask(view: UIView, x: CGFloat, y: CGFloat, radius: CGFloat, downloadRect: CGRect){
    self.layer.sublayers?.forEach { ($0 as? CAShapeLayer)?.removeFromSuperlayer() }

    let mutablePath      = CGMutablePath()
    mutablePath.addArc(center: CGPoint(x: x, y: y + radius), radius: radius, startAngle: 0.0, endAngle: 2 * 3.14, clockwise: false)
    mutablePath.addRect(view.bounds)
    let path             = UIBezierPath(roundedRect: downloadRect, byRoundingCorners: [.topLeft, .bottomRight], cornerRadii: CGSize(width: 5, height: 5))
    mutablePath.addPath(path.cgPath)

    let mask             = CAShapeLayer()
    mask.path            = mutablePath
    mask.fillRule        = kCAFillRuleEvenOdd
    mask.backgroundColor = UIColor.clear.cgColor


    view.layer.mask      = mask
}

传递相同的UIView,它会移除先前的图层并在同一个UIView上应用新的蒙版。

此处mask.fillRule = kCAFillRuleEvenOdd非常重要。如果您注意到有3 mutablePath.addPath()个函数,kCAFillRuleEvenOdd的作用是什么,它首先会创建一个带弧的孔然后添加该视图边界的Rect,然后再添加另一个蒙版来创建第二个洞。

答案 1 :(得分:2)

根据@ Sharad的回答,我意识到重新添加视图的rect可以让我将原始和新的蒙版合并为一个。

这是我的解决方案:

func cutCircle(inView view: UIView, withRect rect: CGRect) {

    // Create new path and mask
    let newMask = CAShapeLayer()
    let newPath = UIBezierPath(ovalIn: rect)

    // Create path to clip
    let newClipPath = UIBezierPath(rect: view.bounds)
    newClipPath.append(newPath)

    // If view already has a mask
    if let originalMask = view.layer.mask,
        let originalShape = originalMask as? CAShapeLayer,
        let originalPath = originalShape.path {

        // Create bezierpath from original mask's path
        let originalBezierPath = UIBezierPath(cgPath: originalPath)

        // Append view's bounds to "reset" the mask path before we re-apply the original
        newClipPath.append(UIBezierPath(rect: view.bounds))

        // Combine new and original paths
        newClipPath.append(originalBezierPath)

    }

    // Apply new mask
    newMask.path = newClipPath.cgPath
    newMask.fillRule = kCAFillRuleEvenOdd
    view.layer.mask = newMask
}

答案 2 :(得分:0)

如果您不仅具有“简单的形状”,而且具有来自例如其他视图,例如UILabelUIImageView

let maskLayer = CALayer()
maskLayer.frame = viewToBeMasked.bounds
maskLayer.addSublayer(self.imageView.layer)
maskLayer.addSublayer(self.label.layer)

viewToBeMasked.bounds.layer.mask = maskLayer

所以基本上,我只是创建一个maskLayer,它包含其他视图的所有层作为子层,然后将其用作遮罩。