Swift - Cut hole in shadow layer

2020-02-29 22:50发布

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:

  • one UIImageView
  • one UIView on top of that image ("overlay")

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:

image

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?

3条回答
爱情/是我丢掉的垃圾
2楼-- · 2020-02-29 23:18

Try using this as as your shadow path:

let shadowWidth = 2.0 // Do this as wide as you want
var outterPath = UIBezierPath()
outterPath.move(to: CGPoint(x: shadowWidth, y: 0))
outterPath.addLine(to: CGPoint(x: glowView.bounds.size.width, y: 0))
outterPath.addLine(to: CGPoint(x: glowView.bounds.size.width, y: glowView.bounds.size.height))
outterPath.addLine(to: CGPoint(x: 0.0, y: glowView.bounds.size.height))
outterPath.addLine(to: CGPoint(x: 0.0, y: 0.0))
outterPath.addLine(to: CGPoint(x: shadowWidth, y: 0.0))
outterPath.addLine(to: CGPoint(x: shadowWidth, y: glowView.bounds.size.height - shadowWidth))
outterPath.addLine(to: CGPoint(x: glowView.bounds.size.width - shadowWidth, y: glowView.bounds.size.height - shadowWidth))
outterPath.addLine(to: CGPoint(x: glowView.bounds.size.width - shadowWidth, y: shadowWidth))
outterPath.addLine(to: CGPoint(x: shadowWidth, y: shadowWidth))
outterPath.close()

This won't create a rounded rect but with a little bit of changes to the code above you should be able to add those too.

查看更多
SAY GOODBYE
3楼-- · 2020-02-29 23:18

Thanks to Mihai Fratu's answer I was able to create a UIBezierPath that exactly meets my needs
I post my code here in case somebody has the same problem later

let innerRadius: CGFloat = 32.0 * UIScreen.main.scale
let shadowPath: UIBezierPath = UIBezierPath(roundedRect: self.view.bounds, cornerRadius: self.cornerRadius)
//shadowPath.append(UIBezierPath(roundedRect: self.view.bounds.insetBy(dx: 8, dy: 8), cornerRadius: self.cornerRadius))
let shadowWidth: CGFloat = 8.0 // Do this as wide as you want
var outterPath = UIBezierPath()
// Start at the top left corner with an x offset of the cornerRadius
outterPath.move(to: CGPoint(x: self.cornerRadius, y: 0))

// Draw a line to the top right corner
outterPath.addLine(to: CGPoint(x: glowView.bounds.size.width - self.cornerRadius, y: 0))
//Draw the round top right corner
outterPath.addArc(withCenter: CGPoint(x: glowView.bounds.size.width - self.cornerRadius, y: self.cornerRadius), radius: self.cornerRadius, startAngle: (3 * CGFloat.pi) / 2, endAngle: 0, clockwise: true)

// Draw a line to the bottom right corner
outterPath.addLine(to: CGPoint(x: glowView.bounds.size.width, y: glowView.bounds.size.height - self.cornerRadius))
// Draw the round bottom right corner
outterPath.addArc(withCenter: CGPoint(x: glowView.bounds.size.width - self.cornerRadius, y: glowView.bounds.size.height -  self.cornerRadius), radius: self.cornerRadius, startAngle: 0, endAngle: CGFloat.pi / 2, clockwise: true)

// Draw a line to the bottom left corner
outterPath.addLine(to: CGPoint(x: self.cornerRadius, y: glowView.bounds.size.height))
// Draw the round bottom left corner
outterPath.addArc(withCenter: CGPoint(x: self.cornerRadius, y: glowView.bounds.size.height -  self.cornerRadius), radius: self.cornerRadius, startAngle: CGFloat.pi / 2, endAngle: CGFloat.pi, clockwise: true)

// Draw a line to the top left corner
outterPath.addLine(to: CGPoint(x: 0.0, y: self.cornerRadius))
// Draw the round top left corner
outterPath.addArc(withCenter: CGPoint(x: self.cornerRadius, y: self.cornerRadius), radius: self.cornerRadius, startAngle: CGFloat.pi, endAngle: (3 * CGFloat.pi) / 2, clockwise: true)

// Move to the inner start point and add the paths counterclockwise to prevent the filling of the inner area
outterPath.move(to: CGPoint(x: shadowWidth + innerRadius, y: shadowWidth))
// Draw the inner top left corner
outterPath.addArc(withCenter: CGPoint(x: shadowWidth + innerRadius, y: shadowWidth + innerRadius), radius: innerRadius, startAngle: (3 * CGFloat.pi) / 2, endAngle: CGFloat.pi, clockwise: false)

// Draw a line to the inner bottom left corner
outterPath.addLine(to: CGPoint(x: shadowWidth, y: glowView.bounds.size.height - innerRadius - shadowWidth))
// Draw the inner bottom left corner
outterPath.addArc(withCenter: CGPoint(x: shadowWidth + innerRadius, y: glowView.bounds.size.height - innerRadius - shadowWidth), radius: innerRadius, startAngle: CGFloat.pi, endAngle: CGFloat.pi / 2, clockwise: false)

// Draw a line to the inner bottom right corner
outterPath.addLine(to: CGPoint(x: glowView.bounds.size.width - innerRadius - shadowWidth, y: glowView.bounds.size.height - shadowWidth))
// Draw the inner bottom right corner
outterPath.addArc(withCenter: CGPoint(x: glowView.bounds.size.width - innerRadius - shadowWidth, y: glowView.bounds.size.height - innerRadius - shadowWidth), radius: innerRadius, startAngle: CGFloat.pi / 2, endAngle: 0, clockwise: false)

// Draw a line to the inner top right corner
outterPath.addLine(to: CGPoint(x: glowView.bounds.size.width - shadowWidth, y: shadowWidth + innerRadius))
// Draw the inner top right corner
outterPath.addArc(withCenter: CGPoint(x: glowView.bounds.size.width - innerRadius - shadowWidth, y: shadowWidth + innerRadius), radius: innerRadius, startAngle: 0, endAngle: (3 * CGFloat.pi) / 2, clockwise: false)

// Draw a line to the inner top left corner
outterPath.addLine(to: CGPoint(x: shadowWidth + innerRadius, y: shadowWidth))
outterPath.close()
查看更多
你好瞎i
4楼-- · 2020-02-29 23:20

These days it's very easy to do this.

enter image description here

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
    }
}
查看更多
登录 后发表回答