Тени маскируются до границ только с левой и верхней стороны. Что вызывает это? - PullRequest
3 голосов
/ 13 марта 2020

Я пытаюсь реализовать функцию, которая позволяет добавлять тень к прозрачной кнопке. Для этого я создаю слой, который маскирует тени внутри вида. Тем не менее, мои тени обрезаются с левой и верхней сторон, но не с правой и нижней сторон.

Вот как это выглядит (Это не прозрачная кнопка, но они также работают нормально, за исключением того, что тень обрезается вот так.):

enter image description here

И вот мой код для достижения этой цели:

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
        }
    }
}

Я думаю, что это связано с алгоритмом четного-нечетного правила заполнения, я не уверен. Но как я могу преодолеть эту проблему отсечения?

Заранее спасибо.

РЕДАКТИРОВАТЬ: Это то, что прозрачная кнопка с границами и текстом на ней выглядит при применении тени .. Что я не хочу.

enter image description here

Как это должно выглядеть. Никаких теней внутри, но также чистый фоновый цвет. (За исключением обрезанной верхней и левой сторон):

enter image description here

1 Ответ

1 голос
/ 13 марта 2020

Я думаю, что пара проблем ...

  1. Вы добавляете UIBezierPath(rect: UIScreen.main.bounds) к своему пути, но это помещает верхний левый угол в верхний левый угол слоя ... который "обрезает" верхнюю левую тень.

  2. Если у вас DO есть цвет фона, вам также нужно обрезать эти углы, или они будут «кровоточить» за пределами закругленных углов.

Попробуйте (только слегка измененные). Он обозначен @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
            }
        }

    }

}

Результат:

enter image description here

...