I want to "cut a hole" in the shadow layer of a UIView an Swift3, iOS
I have a container (UIView), that has 2 children:
I want to give the overlay a shadow and cut out an inner rect of that shadow, to create a glow-like effect at the edges of the ImageView
It is crucial that the glow is inset, since the image is taking the screen width
My code so far:
let glowView = UIView(frame: CGRect(x: 0, y: 0, width: imageWidth, height: imageHeight))
glowView.layer.shadowPath = UIBezierPath(roundedRect: container.bounds, cornerRadius: 4.0).cgPath
glowView.layer.shouldRasterize = true
glowView.layer.rasterizationScale = UIScreen.main.scale
glowView.layer.shadowOffset = CGSize(width: 1.0, height: 1.0)
glowView.layer.shadowOpacity = 0.4
container.addSubview(imageView)
container.addSubview(glowView)
The result looks like the following right now:
Now I would like to cut out the darker inner part, so that just the shadow at the edges remains
Any idea how to achieve this?
These days it's very easy to do this:
Here's the whole thing
import UIKit
class GlowBox: UIView {
override func layoutSubviews() {
super.layoutSubviews()
backgroundColor = .clear
layer.shadowOpacity = 1
layer.shadowColor = UIColor.red.cgColor
layer.shadowOffset = CGSize(width: 0, height: 0)
layer.shadowRadius = 3
let p = UIBezierPath(
roundedRect: bounds.insetBy(dx: 0, dy: 0),
cornerRadius: 4)
let hole = UIBezierPath(
roundedRect: bounds.insetBy(dx: 2, dy: 2),
cornerRadius: 3)
.reversing()
p.append(hole)
layer.shadowPath = p.cgPath
}
}
When you add (that is .append
) two bezier paths like that...
The second one has to be either "normal" or "reversed".
Notice the line of code near the end:
.reversing()
Like most programmers, I can NEVER REMEMBER if it should be "normal" or "reversed" in different cases!!
The simple solution ...
Very simply, try both!
Simply try it with, and without, the .reversing()
It will work one way or the other! :)
override func layoutSubviews() {
super.layoutSubviews()
// take EXTREME CARE of frame vs. bounds
// and + vs - throughout this function:
let _rad: CGRect = 42
colorAndShadow.frame = bounds
colorAndShadow.path =
UIBezierPath(roundedRect: bounds, cornerRadius: _rad).cgPath
let enuff: CGFloat = 200
shadowHole.frame = colorAndShadow.frame.insetBy(dx: -enuff, dy: -enuff)
let _sb = shadowHole.bounds
let p = UIBezierPath(rect: _sb)
let h = UIBezierPath(
roundedRect: _sb.insetBy(dx: enuff, dy: enuff),
cornerRadius: _rad)
p.append(h)
shadowHole.fillRule = .evenOdd
shadowHole.path = p.cgPath
layer.mask = shadowHole
}
shadowHole
is a CAShapeLayer that you must set up using a lazy variable in the usual way.