I am trying programmatically create a layer with transparent text. Everything I try doesn't seem to work. My end goal is to create an inner shadow on text.
Instead of a circle as in the code below I want text.
let view = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
view.backgroundColor = .white
// MASK
let blackSquare = UIView(frame: CGRect(x: 100, y: 100, width: 200, height: 200))
blackSquare.backgroundColor = .black
view.addSubview(blackSquare)
let maskLayer = CAShapeLayer()
// Create a path with the rectangle in it.
let path = CGMutablePath()
path.addArc(center: CGPoint(x: 100, y: 100), radius: 50, startAngle: 0.0, endAngle: 2.0 * .pi, clockwise: false)
path.addRect(CGRect(x: 0, y: 0, width: blackSquare.frame.width, height: blackSquare.frame.height))
maskLayer.backgroundColor = UIColor.black.cgColor
maskLayer.path = path;
maskLayer.fillRule = kCAFillRuleEvenOdd
blackSquare.layer.mask = maskLayer
maskLayer.masksToBounds = false
maskLayer.shadowRadius = 4
maskLayer.shadowOpacity = 0.5
maskLayer.shadowOffset = CGSize(width: 5, height: 5)
view.addSubview(blackSquare)
I'm also able to use text as a mask but I'm unable to invert the mask. Any help is appreciated.
EDIT:
I figured it out based on Rob's answer as suggested by Josh. Here's my playground code.
import Foundation
import UIKit
import PlaygroundSupport
// view
let view = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
view.backgroundColor = .black
// button
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
button.setTitle("120", for: .normal)
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = UIFont(name: "AvenirNextCondensed-UltraLight", size: 200)
view.addSubview(button)
addInnerShadow(button: button)
func addInnerShadow(button: UIButton) {
// text
let text = button.titleLabel!.text!
// get context
UIGraphicsBeginImageContextWithOptions(button.bounds.size, true, 0)
let context = UIGraphicsGetCurrentContext()
context?.scaleBy(x: 1, y: -1)
context?.translateBy(x: 0, y: -button.bounds.size.height)
let font = button.titleLabel!.font!
// draw the text
let attributes = [
NSAttributedStringKey.font: font,
NSAttributedStringKey.foregroundColor: UIColor.white
]
let size = text.size(withAttributes: attributes)
let point = CGPoint(x: (button.bounds.size.width - size.width) / 2.0, y: (button.bounds.size.height - size.height) / 2.0)
text.draw(at: point, withAttributes: attributes)
// capture the image and end context
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// create image mask
let cgimage = image?.cgImage!
let bytesPerRow = cgimage?.bytesPerRow
let dataProvider = cgimage?.dataProvider!
let bitsPerPixel = cgimage?.bitsPerPixel
let width = cgimage?.width
let height = cgimage?.height
let bitsPerComponent = cgimage?.bitsPerComponent
let mask = CGImage(maskWidth: width!, height: height!, bitsPerComponent: bitsPerComponent!, bitsPerPixel: bitsPerPixel!, bytesPerRow: bytesPerRow!, provider: dataProvider!, decode: nil, shouldInterpolate: false)
// create background
UIGraphicsBeginImageContextWithOptions(button.bounds.size, false, 0)
UIGraphicsGetCurrentContext()!.clip(to: button.bounds, mask: mask!)
view.backgroundColor!.setFill()
UIBezierPath(rect: button.bounds).fill()
let background = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let backgroundView = UIImageView(image: background)
// add shadows
backgroundView.layer.shadowOffset = CGSize(width: 2, height: 2)
backgroundView.layer.shadowRadius = 2
backgroundView.layer.shadowOpacity = 0.75
button.addSubview(backgroundView)
}
PlaygroundPage.current.liveView = view