Search code examples
iostransparencycalayermask

Semi transparent dynamic mask on over dynamic UIView


Not sure where to look for this but I want to create a semi transparent mask of a view or layer that can move, change opacity and have multiple on screen at once that overlap.

Something to this effect:

enter image description here

Where the two small boxes can move around and change the opacity of the colour.

I have tried with CALayer mask but that is just punch through so I added a CAShapeLayer in the hole but you can see the edges. I tried using allowsEdgeAntialiasing on the CAShapeLayer but that didn't seem to work.

Other examples I've seen only work with UIImageViews because they use two UIImageViews on top of each other to get the effect. Unfortunately I need this to work over any kind of UIView.

I also tried using CGBlendMode.SourceOut which works well except the overlapping areas are lighter, which makes total sense but not what I'm after.

let blendMode = CGBlendMode.SourceOut

CGContextSaveGState(context)

CGContextSetFillColorWithColor(context, UIColor(white: 0.0, alpha: 0.75).CGColor)
CGContextFillRect(context, rect)

CGContextSetBlendMode(context, blendMode)

CGContextSetFillColorWithColor(context, UIColor(white: 0.0, alpha: 0.25).CGColor)
CGContextFillRect(context, CGRectMake(0, 500, 60, 50))

CGContextSetFillColorWithColor(context, UIColor(white: 0.0, alpha: 1).CGColor)
CGContextFillRect(context, CGRectMake(0, 550, 60, 50))

The last two fill rects overlap and the first is to set the entire view to have a semi transparent overlay.


Solution

  • Looks like you can use the blend modes:

    • Normal for the background
    • Clear for holes for the squares
    • Normal again for the squares to give the overlapping look

    enter image description here

    This doesn't appear to show edges around the squares when the second box is set to the same alpha as the background

    enter image description here

    This code generates the top image (called in drawRect):

    CGContextSetBlendMode(context, CGBlendMode.Normal)
    CGContextSetFillColorWithColor(context, UIColor(white: 0.0, alpha: 0.75).CGColor)
    CGContextFillRect(context, rect)
    
    CGContextSetBlendMode(context, CGBlendMode.Clear)
    CGContextSetFillColorWithColor(context, UIColor(white: 0.0, alpha: 1).CGColor)
    CGContextFillRect(context, CGRectMake(20, 150, 100, 100))
    CGContextSetFillColorWithColor(context, UIColor(white: 0.0, alpha: 1).CGColor)
    CGContextFillRect(context, CGRectMake(50, 200, 100, 100))
    
    CGContextSetBlendMode(context, CGBlendMode.Normal)
    CGContextSetFillColorWithColor(context, UIColor(white: 0.0, alpha: 0.25).CGColor)
    CGContextFillRect(context, CGRectMake(20, 150, 100, 100))
    CGContextSetFillColorWithColor(context, UIColor(white: 0.0, alpha: 0.15).CGColor)
    CGContextFillRect(context, CGRectMake(50, 200, 100, 100))