使用 UIBezierPath 为带有阴影的选定边添加圆角半径

Adding Corner radius for selected edges with shadow using UIBezierPath

我正在尝试在我的视图中添加阴影。它有 3 个圆形边缘。实现这个 UIBezierPath,然后将这个路径的 CAShapelayer 设置为视图层上的遮罩。现在,如果我试图在此视图上添加阴影,它不会显示。我已经经历了类似的问题和建议的答案,但在我的情况下没有任何效果。以下是我的实现:

class BubbleView: UIView {

override func draw(_ rect: CGRect) {
    super.draw(rect)
}

override func layoutSubviews() {
    super.layoutSubviews()
    self.updateContainerLayer()
}

func updateContainerLayer() {
    let brazierPath: UIBezierPath = UIBezierPath(roundedRect: self.bounds,
                                                 byRoundingCorners: [.bottomRight, .bottomLeft, .topLeft],
                                                 cornerRadii: CGSize(width: 15.0, height: 0.0))

    //1
    let shapeLayer = CAShapeLayer()
    shapeLayer.path = brazierPath.cgPath
    self.layer.mask = shapeLayer

    //2
    self.layer.shadowColor  = UIColor(r: 0, g: 0, b: 0, alpha: 0.25).cgColor
    self.layer.shadowOpacity = 1.0
    self.layer.shadowOffset = CGSize(width: 0.0, height: 0.5)
    self.layer.shadowRadius = 1.5
    self.layer.shadowPath   = brazierPath.cgPath
    //3
    self.layer.masksToBounds = true
    self.clipsToBounds = false
    //4
    self.layer.shouldRasterize = true
    self.layer.rasterizationScale = UIScreen.main.scale


}

}

如果您的目标是 iOS 11+ 您可以使用图层的 .maskedCorners 属性:

class BubbleView: UIView {

    // don't override draw()
//  override func draw(_ rect: CGRect) {
//      super.draw(rect)
//  }

    override func layoutSubviews() {
        super.layoutSubviews()
        self.updateContainerLayer()
    }

    func updateContainerLayer() {
        let brazierPath: UIBezierPath = UIBezierPath(roundedRect: self.bounds,
                                                     byRoundingCorners: [.bottomRight, .bottomLeft, .topLeft],
                                                     cornerRadii: CGSize(width: 15.0, height: 0.0))

        //1
//      let shapeLayer = CAShapeLayer()
//      shapeLayer.path = brazierPath.cgPath
//      self.layer.mask = shapeLayer

        // iOS 11+ use .maskedCorners
        self.layer.cornerRadius = 15.0
        self.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner]

        //2
        self.layer.shadowColor  = UIColor(red: 0, green: 0, blue: 0, alpha: 0.25).cgColor
        self.layer.shadowOpacity = 1.0
        self.layer.shadowOffset = CGSize(width: 0.0, height: 0.5)
        self.layer.shadowRadius = 1.5
        self.layer.shadowPath   = brazierPath.cgPath
        //3
        self.layer.masksToBounds = true
        self.clipsToBounds = false
        //4
        self.layer.shouldRasterize = true
        self.layer.rasterizationScale = UIScreen.main.scale


    }
}

结果:

夸张的结果 .shadowOffset = CGSize(width: -10.0, height: 10.5) 以便于看到阴影:

如果您需要允许更早的 iOS 版本,我 相信 您需要使用容器视图方法。


编辑:

另一种方法,对阴影使用 "container" 视图。这将适用于早于 11 的 iOS...它对 "content view" 掩码和阴影路径使用相同的 UIBezierPath

class BubbleView: UIView {

let contentView: UIView = {
    let v = UIView()
    v.translatesAutoresizingMaskIntoConstraints = false
    return v
}()

override init(frame: CGRect) {
    super.init(frame: frame)
    commonInit()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    commonInit()
}

func commonInit() -> Void {

    addSubview(contentView)

    NSLayoutConstraint.activate([
        contentView.topAnchor.constraint(equalTo: topAnchor),
        contentView.bottomAnchor.constraint(equalTo: bottomAnchor),
        contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
        contentView.trailingAnchor.constraint(equalTo: trailingAnchor),
        ])

    self.clipsToBounds = false

    backgroundColor = .clear
    contentView.backgroundColor = .red

    // set non-changing properties here
    contentView.layer.masksToBounds = true

    self.layer.shadowColor  = UIColor(red: 0, green: 0, blue: 0, alpha: 0.25).cgColor
    self.layer.shadowOpacity = 1.0

    self.layer.shadowOffset = CGSize(width: 0.0, height: 0.5)

    // exaggerated shadow offset so we can see it easily
    //self.layer.shadowOffset = CGSize(width: -10.0, height: 10.5)

    self.layer.shadowRadius = 1.5

    self.layer.shouldRasterize = true
    self.layer.rasterizationScale = UIScreen.main.scale

}

override func layoutSubviews() {
    super.layoutSubviews()

    let bezierPath = UIBezierPath(roundedRect: self.bounds,
                                  byRoundingCorners: [.bottomRight, .bottomLeft, .topLeft],
                                  cornerRadii: CGSize(width: 15.0, height: 0.0))

    let shapeLayer = CAShapeLayer()
    shapeLayer.path = bezierPath.cgPath
    contentView.layer.mask = shapeLayer

    self.layer.shadowPath   = bezierPath.cgPath

}

}

与 11+ 示例一样,结果:

并且结果带有夸张的 .shadowOffset = CGSize(width: -10.0, height: 10.5) 以便于看到阴影: