具有背景图像、圆角和阴影的 UIButton

UIButton with background image, rounded corners and a shadow

我正在尝试创建一个带有圆角、背景图像和阴影的 UIButton。在添加阴影之前,一切正常。

但是添加阴影值后,阴影没有出现。显然是由于 clipsToBounds 属性 值被设置为 true。如果我删除它,它看起来像这样。

因为我也需要圆角半径,所以我不能让 clipsToBoundsfalse

这是我的代码。

class CustomButton: UIButton {
    
    var cornerRadius: CGFloat {
        get {
            return layer.cornerRadius
        }
        set {
            layer.cornerRadius = newValue
            clipsToBounds = true
        }
    }
    
    var shadowRadius: CGFloat {
        get {
            return layer.shadowRadius
        }
        set {
            layer.shadowRadius = newValue
        }
    }
    
    var shadowOpacity: Float {
        get {
            return layer.shadowOpacity
        }
        set {
            layer.shadowOpacity = newValue
        }
    }
    
    var shadowOffset: CGSize {
        get {
            return layer.shadowOffset
        }
        set {
            layer.shadowOffset = newValue
        }
    }
    
    var shadowColor: UIColor? {
        get {
            if let color = layer.shadowColor {
                return UIColor(cgColor: color)
            }
            return nil
        }
        set {
            if let color = newValue {
                layer.shadowColor = color.cgColor
            } else {
                layer.shadowColor = nil
            }
        }
    }
    
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        
    }
    
}


private lazy var button: CustomButton = {
    let button = CustomButton()
    button.translatesAutoresizingMaskIntoConstraints = false
    button.setBackgroundImage(UIImage(named: "Rectangle"), for: .normal)
    button.setTitleColor(.white, for: .normal)
    button.setTitle("Sign Up", for: .normal)
    button.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: .semibold)
    button.cornerRadius = 20
    button.shadowColor = .systemGreen
    button.shadowRadius = 10
    button.shadowOpacity = 1
    button.shadowOffset = CGSize(width: 0, height: 0)
    return button
}()

是否有同时具有阴影和角半径的解决方法?

Demo project

您可以添加 view.layer.masksToBounds = false

这将禁用子层的裁剪

https://developer.apple.com/documentation/quartzcore/calayer/1410896-maskstobounds

您需要为阴影和图像使用两个单独的视图。我找不到任何使用相同按钮层设置图像、阴影和圆角半径的解决方案。

使按钮的角半径(clipsToBounds=true) 变圆并在其上设置图像。

在具有适当阴影半径和偏移的按钮下拍摄阴影视图。

您可以通过添加不同图层的阴影和背景图像来实现。

首先,如果您不需要这些属性,请删除所有属性并修改您的 CustomButton 实现,如下所示(根据需要修改):

class CustomButton: UIButton {
    
    private let cornerRadius: CGFloat = 20
    private var imageLayer: CALayer!
    private var shadowLayer: CALayer!
    
    override func draw(_ rect: CGRect) {
        addShadowsLayers(rect)
    }
    
    private func addShadowsLayers(_ rect: CGRect) {
        // Add Image
        if self.imageLayer == nil {
            let imageLayer = CALayer()
            imageLayer.frame = rect
            imageLayer.contents = UIImage(named: "Rectangle")?.cgImage
            imageLayer.cornerRadius = cornerRadius
            imageLayer.masksToBounds = true
            layer.insertSublayer(imageLayer, at: 0)
            self.imageLayer = imageLayer
        }
        
        // Set the shadow
        if self.shadowLayer == nil {
            let shadowLayer = CALayer()
            shadowLayer.masksToBounds = false
            shadowLayer.shadowColor = UIColor.systemGreen.cgColor
            shadowLayer.shadowOffset = .zero
            shadowLayer.shadowOpacity = 1
            shadowLayer.shadowRadius = 10
            shadowLayer.shadowPath = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).cgPath
            layer.insertSublayer(shadowLayer, at: 0)
            self.shadowLayer = shadowLayer
        }
    }
}

然后像下面这样初始化你的按钮:

private lazy var button: CustomButton = {
    let button = CustomButton()
    button.translatesAutoresizingMaskIntoConstraints = false
    button.setTitleColor(.white, for: .normal)
    button.setTitle("Sign Up", for: .normal)
    button.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: .semibold)
    return button
}()