如何为UIView应用多个遮罩

9
我有一个问题,关于如何将多个遮罩应用到已经有遮罩的UIView上。
情况:我有一个带有活跃遮罩的视图,它在其左上角创建了一个洞。这是一个模板UIView,在项目中随处重复使用。在项目后期,我想能够在右下角创建第二个洞,而不需要创建全新的UIView。
问题:当我应用底部遮罩时,它当然替换了第一个遮罩,因此删除了顶部洞口...是否有一种方法可以将它们结合起来?对于任何现有的遮罩和新遮罩是否都适用?
谢谢!
3个回答

8

基于@Sharad的答案,我意识到重新添加视图的矩形将使我能够将原始和新的掩码合并为一个。

这是我的解决方案:

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
}

非常简单的解决方案...谢谢! 对于那些想要矩形而不是圆形的人,只需更改为: let newPath = UIBezierPath(rect: rect) - Tiago Mendes

4

以下是我在项目中使用的代码,用于在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做的是,它首先使用弧形创建一个孔,然后添加该视图边界的矩形,再添加另一个遮罩以创建第二个孔。


1
非常感谢!虽然您的解决方案似乎是删除并重新创建所有形状,但我注意到在弧线和路径之间添加了视图的矩形,这正是我所缺少的!我添加了一个更全局的解决方案,似乎可以在不知道先前蒙版如何创建的情况下工作! - Thomas

2
你可以像下面这样做,如果你不仅仅有"简单形状",而是来自其他视图(例如UILabelUIImageView)的实际图层。
let maskLayer = CALayer()
maskLayer.frame = viewToBeMasked.bounds
maskLayer.addSublayer(self.imageView.layer)
maskLayer.addSublayer(self.label.layer)

viewToBeMasked.layer.mask = maskLayer

所以基本上我只需要创建一个maskLayer,其中包含所有其他视图的子层,并将其用作蒙版。


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