Search code examples
iosuilabel

Reverse layer mask for label


How do you reverse the mask layer for a label? I have a textLabel, which I use as a mask for an imageView that contains an arbitrary image as follows:

let image = UIImage(named: "someImage")
let imageView = UIImageView(image: image!)

let textLabel = UILabel()
textLabel.frame = imageView.bounds
textLabel.text = "Some text"

imageView.layer.mask = textLabel.layer
imageView.layer.masksToBounds = true

The above makes the text in textLabel have a font colour of the imageView as in How to mask the layer of a view by the content of another view?.

How do I reverse this so as to remove the text in textLabel from the imageView?


Solution

  • Make a subclass of UILabel:

    class InvertedMaskLabel: UILabel {
        override func drawTextInRect(rect: CGRect) {
            guard let gc = UIGraphicsGetCurrentContext() else { return }
            CGContextSaveGState(gc)
            UIColor.whiteColor().setFill()
            UIRectFill(rect)
            CGContextSetBlendMode(gc, .Clear)
            super.drawTextInRect(rect)
            CGContextRestoreGState(gc)
        }
    }
    

    This subclass fills its bounds with an opaque color (white in this example, but only the alpha channel matters). Then it draws the text using the Clear blend mode, which simply sets all channels of the context back to 0, including the alpha channel.

    Playground demo:

    let root = UIView(frame: CGRectMake(0, 0, 400, 400))
    root.backgroundColor = .blueColor()
    XCPlaygroundPage.currentPage.liveView = root
    
    let image = UIImage(named: "Kaz-256.jpg")
    let imageView = UIImageView(image: image)
    root.addSubview(imageView)
    
    let label = InvertedMaskLabel()
    label.text = "Label"
    label.frame = imageView.bounds
    label.font = .systemFontOfSize(40)
    imageView.maskView = label
    

    Result:

    demo of image transparency inside the label text