Search code examples
iosswiftuiviewmaskcashapelayer

How to apply multiple masks to UIView


I have a question about how to apply multiple masks to a UIView that already has a mask.

The situation:
I have a view with an active mask that creates a hole in its top left corner, this is a template UIView that is reused everywhere in the project. Later in the project I would like to be able to create a second hole but this time in the bottom right corner, this without the need to create a completely new UIView.

The problem:
When I apply the bottom mask, it of course replaces the first one thus removing the top hole ... Is there a way to combine them both? And for that matter to combine any existing mask with a new one?

Thank you in advance!


Solution

  • Based on @Sharad's answer, I realised that re-adding the view's rect would enable me to combine the original and new mask into one.

    Here is my solution:

    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
    }