I want to add multiple shadows to a label. Red shadow and blue shadow. But my code doesn't work. I see only blue shadow.
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.textColor = .black
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
titleLabel.text = "1"
addDropShadow(color: UIColor.red, offset: CGSize(width: -6, height: -6), btnLayer: titleLabel.layer)
addDropShadow(color: UIColor.blue, offset: CGSize(width: 6, height: 6), btnLayer: titleLabel.layer)
}
private func addDropShadow(color: UIColor, offset: CGSize, btnLayer : CALayer) {
btnLayer.masksToBounds = false
btnLayer.shadowColor = color.cgColor
btnLayer.shadowOpacity = 1
btnLayer.shadowOffset = offset
btnLayer.shadowRadius = 10
}
Add new code. But this case not work for me too. How to fix it? I can't see shadows at all
let layer1 = CALayer()
let layer2 = CALayer()
titleLabel.text = "1"
layer1.shadowColor = UIColor.black.cgColor
layer1.shadowOpacity = 0.4
layer1.shadowOffset = CGSize.zero
layer2.shadowRadius = 4
layer2.shadowColor = UIColor.blue.cgColor
layer2.shadowOpacity = 0.4
layer2.shadowOffset = CGSize.zero
layer2.shadowRadius = 4
layer3.shadowColor = UIColor.green.cgColor
layer3.shadowOpacity = 0.4
layer3.shadowOffset = CGSize.zero
layer3.shadowRadius = 4
titleLabel.layer.insertSublayer(layer1, at: 1)
titleLabel.layer.insertSublayer(layer2, at: 2)
titleLabel.layer.insertSublayer(layer2, at: 3)
I want to add some shadows for the label like this result(2 shadows, red and blue):
One option is to create two labels, one with the red shadow and one with the blue shadow, and overlay them:
Obviously, adjust the color, alpha, offset and radius to your liking.
A second option could be to generate an image of the text with each color, apply a gaussian blur, and then layer them together.
Edit - in response to comments...
Your attempt with 3 sublayers doesn't do anything, because to get a shadow to show up on a layer, we need to either give that layer a .shadowPath
(which we're not getting into here), or the layer must have something to "cast a shadow" (it doesn't really, but it's a good way to think about it).
So, if we use this simple example:
class ShadowTutorialVC: UIViewController {
let theLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
theLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(theLabel)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
theLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
theLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
])
if let testFont: UIFont = UIFont(name: "MarkerFelt-Thin", size: 40.0) {
theLabel.font = testFont
} else {
theLabel.font = .systemFont(ofSize: 40.0, weight: .regular)
}
theLabel.text = "Type Something"
theLabel.layer.shadowColor = UIColor.systemBlue.cgColor
theLabel.layer.shadowOpacity = 1.0
theLabel.layer.shadowOffset = .init(width: 12.0, height: 12.0)
theLabel.layer.shadowRadius = 2.0
}
}
we get this output:
If the .text
of that label is "" (no text), it will be empty with NO shadow.
Note that if we set the background color of the label (to very light gray):
theLabel.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
we get this output:
That's because the label's .layer
is now a solid color rectangle.
Likewise, if you try to set the .shadow
properties twice on the same layer, only the second settings will show up ... it's only a single layer with character glyphs on it.
There are various ways to get your desired "double shadow" output... one method would be to get the character glyphs as a CGPath
, then create two CALayers
using that path as the .shadowPath
.
An easier approach - as I described here - is to overlay a second UILabel
on top of the first, and give each label its own shadow properties.
Pretty straightforward to create a UIView
subclass to accomplish that "automatically".
To make things easy on ourselves, we'll start with a "Shadow Definition" struct:
struct ShadowDef {
var color: UIColor = .black
var opacity: Float = 1.0
var offset: CGSize = .zero
var radius: CGFloat = 2.0
}
Then we create a UIView
subclass:
class DoubleShadowLabel: UIView {
// "typical" label properties... add any others you may want to implment
public var text: String = "" { didSet { updateProperties() } }
public var textColor: UIColor = .black { didSet { updateProperties() } }
public var textAlignment: NSTextAlignment = .center { didSet { updateProperties() } }
public var font: UIFont = .systemFont(ofSize: 17.0) { didSet { updateProperties() } }
// shadow properties we can set from the controller
public var shadow1: ShadowDef = ShadowDef() { didSet { updateProperties() } }
public var shadow2: ShadowDef = ShadowDef() { didSet { updateProperties() } }
// two labels... we will overlay label2 on top of label1
private var label1: UILabel = UILabel()
private var label2: UILabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
// add and constrain the labels
for v in [label1, label2] {
v.translatesAutoresizingMaskIntoConstraints = false
addSubview(v)
NSLayoutConstraint.activate([
v.topAnchor.constraint(equalTo: topAnchor, constant: 0.0),
v.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
v.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),
v.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0.0),
])
}
updateProperties()
}
// this func will set the same "label" properties on both labels
// then the defined "shadow 1 & 2" properties on the respective labels
private func updateProperties() {
for v in [label1, label2] {
v.text = text
v.textColor = textColor
v.textAlignment = textAlignment
v.font = font
}
// set "under" label shadow properties
label1.layer.shadowColor = shadow1.color.cgColor
label1.layer.shadowOpacity = shadow1.opacity
label1.layer.shadowOffset = shadow1.offset
label1.layer.shadowRadius = shadow1.radius
// set "over" label shadow properties
label2.layer.shadowColor = shadow2.color.cgColor
label2.layer.shadowOpacity = shadow2.opacity
label2.layer.shadowOffset = shadow2.offset
label2.layer.shadowRadius = shadow2.radius
}
}
and, an example view controller to see it in action:
class DoubleShadowTestVC: UIViewController {
let testView = DoubleShadowLabel()
override func viewDidLoad() {
super.viewDidLoad()
testView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(testView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
testView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
testView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
])
if let testFont: UIFont = UIFont(name: "MarkerFelt-Thin", size: 40.0) {
testView.font = testFont
} else {
testView.font = .systemFont(ofSize: 40.0, weight: .regular)
}
testView.text = "Type Something"
testView.shadow1 = ShadowDef(color: .systemRed, opacity: 1.0, offset: .init(width: -1.0, height: -1.0), radius: 2.0)
testView.shadow2 = ShadowDef(color: .systemBlue, opacity: 1.0, offset: .init(width: 1.0, height: 1.0), radius: 2.0)
}
}
and we get this output:
And it becomes easy to "fine-tune" the shadows with these two lines (I'm exaggerating the offsets for effect):
testView.shadow1 = ShadowDef(color: .systemGreen, opacity: 1.0, offset: .init(width: -10.0, height: -10.0), radius: 3.0)
testView.shadow2 = ShadowDef(color: .systemYellow, opacity: 1.0, offset: .init(width: 10.0, height: 10.0), radius: 3.0)
Result: