使用 UIBezierPath 为带有阴影的选定边缘添加角半径 | iOS |斯威夫特 4.2

Posted

技术标签:

【中文标题】使用 UIBezierPath 为带有阴影的选定边缘添加角半径 | iOS |斯威夫特 4.2【英文标题】:Adding Corner radius for selected edges with shadow using UIBezierPath | iOS | Swift 4.2 【发布时间】:2019-07-12 15:04:22 【问题描述】:

我正在尝试在我的视图上添加阴影。它有 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



【问题讨论】:

【参考方案1】:

如果您的目标是 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) 以便于看到阴影:

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


编辑:

另一种方法,对阴影使用“容器”视图。这适用于早于 11 的 iOS...它使用相同的 UIBezierPath 作为“内容视图”掩码和阴影路径:

类 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+ 示例一样,结果:

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

【讨论】:

感谢您的解决方案。但我也瞄准了 iOS 10。 “容器视图方法”。 - 你能详细说明一下吗? 谢谢!!稍加调整和您的建议,终于完成了!

以上是关于使用 UIBezierPath 为带有阴影的选定边缘添加角半径 | iOS |斯威夫特 4.2的主要内容,如果未能解决你的问题,请参考以下文章

给控件的指定边设置圆角

使用 UIBezierPath 将半径设置为某些角并向自定义 UIButton 添加阴影

使用 UIBezierPath 向 UIView 添加阴影

UIBezierPath 阴影效果

UIBezierPath阴影效果

阴影不适用于带有蒙版的 UIViewLayer