阴影仅在左侧和上侧遮罩边界。是什么原因造成的?

Shadows masks to bounds only on the left and upper side. What causes this?

我正在尝试实现一项允许向透明按钮添加阴影的功能。为此,我正在创建一个图层来掩盖视图内的阴影。但是,我的阴影在左侧和上侧被剪裁,但在右侧和下侧没有剪裁。

这是它的外观(这不是透明按钮,但它们也可以正常工作,只是阴影被剪裁成这样。):

这是我实现此目的的代码:

private func applyShadow() {
    layer.masksToBounds = false

    if shouldApplyShadow && shadowLayer == nil {
        shadowLayer = CAShapeLayer()

        let shapePath = CGPath(roundedRect: bounds, cornerWidth: cornerRadi, cornerHeight: cornerRadi, transform: nil)

        shadowLayer.path = shapePath
        shadowLayer.fillColor = backgroundColor?.cgColor
        shadowLayer.shadowPath = shadowLayer.path
        shadowLayer.shadowRadius = shadowRadius ?? 8
        shadowLayer.shadowColor = (shadowColor ?? .black).cgColor
        shadowLayer.shadowOffset = shadowOffset ?? CGSize(width: 0, height: 0)
        shadowLayer.shadowOpacity = shadowOpacity ?? 0.8

        layer.insertSublayer(shadowLayer!, at: 0)

        /// If there's background color, there is no need to mask inner shadows.
        if backgroundColor != .none && !(innerShadows ?? false) {
            let maskLayer = CAShapeLayer()
            maskLayer.path = { () -> UIBezierPath in
                let path = UIBezierPath()
                path.append(UIBezierPath(cgPath: shapePath))
                path.append(UIBezierPath(rect: UIScreen.main.bounds))
                path.usesEvenOddFillRule = true
                return path
            }().cgPath

            maskLayer.fillRule = .evenOdd
            shadowLayer.mask = maskLayer
        }
    }
}

我认为这与奇偶填充规则算法有关,我不确定。但是我怎样才能克服这个剪裁问题呢?

提前致谢。

编辑: 这是应用阴影时带有边框和文本的透明按钮的外观。我不想要。

它应该是这样的。内部没有阴影,但背景颜色也很清晰。 (除了剪裁的顶部和左侧):

我认为有几个问题...

  1. 您正在将 UIBezierPath(rect: UIScreen.main.bounds) 附加到您的路径,但这会将左上角放在图层的左上角... "clips" 顶部-左影.

  2. 如果您 DO 有背景颜色,您还需要剪掉这些角,否则它们会 "bleed" 在圆角之外角落.

试一试(仅稍作修改)。它被指定为 @IBDesignable,因此您可以在 Storyboard / Interface Builder 中查看它的外观(我没有将任何属性设置为可检查 - 如果您愿意,我会把它留给您):

@IBDesignable
class MyRSButton: UIButton {

    var shouldApplyShadow: Bool = true
    var innerShadows: Bool?

    var cornerRadi: CGFloat = 8.0

    var shadowLayer: CAShapeLayer!
    var shadowRadius: CGFloat?
    var shadowColor: UIColor?
    var shadowOffset: CGSize?
    var shadowOpacity: Float?

    override func layoutSubviews() {
        super.layoutSubviews()
        applyShadow()
    }

    private func applyShadow() {

        // needed to prevent background color from bleeding past
        // the rounded corners
        cornerRadi = bounds.size.height * 0.5
        layer.cornerRadius = cornerRadi

        layer.masksToBounds = false

        if shouldApplyShadow && shadowLayer == nil {
            shadowLayer = CAShapeLayer()

            let shapePath = CGPath(roundedRect: bounds, cornerWidth: cornerRadi, cornerHeight: cornerRadi, transform: nil)

            shadowLayer.path = shapePath
            shadowLayer.fillColor = backgroundColor?.cgColor
            shadowLayer.shadowPath = shadowLayer.path
            shadowLayer.shadowRadius = shadowRadius ?? 8
            shadowLayer.shadowColor = (shadowColor ?? .black).cgColor
            shadowLayer.shadowOffset = shadowOffset ?? CGSize(width: 0, height: 0)
            shadowLayer.shadowOpacity = shadowOpacity ?? 0.8

            layer.insertSublayer(shadowLayer!, at: 0)

            /// If there's background color, there is no need to mask inner shadows.
            if backgroundColor != .none && !(innerShadows ?? false) {
                let maskLayer = CAShapeLayer()
                maskLayer.path = { () -> UIBezierPath in
                    let path = UIBezierPath()
                    path.append(UIBezierPath(cgPath: shapePath))

                    // define a rect that is 80-pts wider and taller
                    // than the button... this will "expand" it from center
                    let r = bounds.insetBy(dx: -40, dy: -40)

                    path.append(UIBezierPath(rect: r))

                    path.usesEvenOddFillRule = true
                    return path
                    }().cgPath

                maskLayer.fillRule = .evenOdd
                shadowLayer.mask = maskLayer
            }
        }

    }

}

结果: