可以将 UIButton 图像设置为零索引吗?

Possible to set UIButton image to be at zero index?

我为 UIButton 创建了一个子class,我在其中绘制了一个 CAShapelayer 作为按钮的圆形背景色,效果很好。然而,CAShapeLayer 现在覆盖了按钮图像。

处理此问题的最佳方法是什么?我试过插入 CAShapeLayer 但没有任何效果。有没有办法将按钮图像置于前台?

这就是我目前投射按钮图片的方式:

var buttonImage: UIImage = UIImage(named: "icon_check")! {
    didSet {
        setImage(buttonImage, for: .normal)
    }
}

更新:整个class的代码:

class PulseButton: UIButton {

let buttonFillLayer = CAShapeLayer()
let pulsatingLayer = CAShapeLayer()

var buttonFillLayerFillColor:   UIColor = .buttonLayerFillColor                                               { didSet { buttonFillLayer.fillColor = buttonFillLayerFillColor.cgColor } }
var pulsatingLayerFillColor:   UIColor = .pulsatingLayerFillColor                                             { didSet { pulsatingLayer.fillColor = pulsatingLayerFillColor.cgColor } }

var buttonFillLayerStrokeStart: CGFloat = 0                                                                   { didSet { buttonFillLayer.strokeStart = buttonFillLayerStrokeStart } }
var pulsatingLayerStrokeStart: CGFloat = 0                                                                    { didSet { pulsatingLayer.strokeStart = pulsatingLayerStrokeStart } }

var buttonFillLayerStrokeEnd:   CGFloat = 1                                                                   { didSet { buttonFillLayer.strokeEnd = buttonFillLayerStrokeEnd } }
var pulsatingLayerStrokeEnd:   CGFloat = 1                                                                    { didSet { pulsatingLayer.strokeEnd = pulsatingLayerStrokeEnd } }

var buttonImage: UIImage = UIImage(named: "icon_check")! {
    didSet {
        setImage(buttonImage, for: .normal)
    }
}

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

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

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

private func setupLayout() {
    pulsatingLayer.strokeColor = UIColor.clear.cgColor
    pulsatingLayer.fillColor = buttonFillLayerFillColor.cgColor
    pulsatingLayer.strokeStart = buttonFillLayerStrokeStart
    pulsatingLayer.strokeEnd  = buttonFillLayerStrokeEnd

    buttonFillLayer.strokeColor = UIColor.clear.cgColor
    buttonFillLayer.fillColor = pulsatingLayerFillColor.cgColor
    buttonFillLayer.strokeStart = pulsatingLayerStrokeStart
    buttonFillLayer.strokeEnd  = pulsatingLayerStrokeEnd

    layer.addSublayer(pulsatingLayer)
    layer.addSublayer(buttonFillLayer)
}

private func updatePaths()  {
    print("Updating")

    //Parameters for layers
    let arcCenter = CGPoint(x: bounds.midX, y: bounds.midY)
    let radius = (min(bounds.width, bounds.height)) / 2
    let path = UIBezierPath(arcCenter: arcCenter, radius: radius, startAngle: 0, endAngle: 2*CGFloat.pi, clockwise: true).cgPath

    pulsatingLayer.transform = CATransform3DIdentity
    pulsatingLayer.frame = bounds
    pulsatingLayer.transform = CATransform3DMakeRotation(-CGFloat.pi/2, 0, 0, 1)
    buttonFillLayer.transform = CATransform3DIdentity
    buttonFillLayer.frame = bounds
    buttonFillLayer.transform = CATransform3DMakeRotation(-CGFloat.pi/2, 0, 0, 1)

    pulsatingLayer.path = path
    buttonFillLayer.path = path
}

func animateCircle() {
    let scaleUpAimation = CABasicAnimation(keyPath: "transform.scale")
    scaleUpAimation.fromValue = 1
    scaleUpAimation.toValue = 1.4
    scaleUpAimation.duration = 0.8
    scaleUpAimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
    self.pulsatingLayer.add(scaleUpAimation, forKey: "pulsing")


    let opacityAnimation = CABasicAnimation(keyPath: "opacity")
    opacityAnimation.fromValue = 0.6
    opacityAnimation.toValue = 0
    opacityAnimation.duration = 0.8
    opacityAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
    pulsatingLayer.add(opacityAnimation, forKey: "changing opacity")
}
}

我创建了一个简单示例,用于将 CAShapeLayer 和 UIImage 添加到 UIButton。

1. UIButton

的扩展
extension UIButton
{
    func addLayer()
    {
        let layer = CAShapeLayer()
        layer.path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 100, height: 100), cornerRadius: 50).cgPath
        layer.fillColor = UIColor.red.cgColor
        self.layer.insertSublayer(layer, at: 0)
    }
} 

2。将 CAShapeLayer 添加到 UIButton

viewWillLayoutSubviews()中添加CAShapeLayer

class ViewController: UIViewController
{
    @IBOutlet weak var button: UIButton!

    var layerAdded = false

    override func viewDidLoad()
    {
        super.viewDidLoad()
        self.button.setImage(#imageLiteral(resourceName: "furnish_exhaust"), for: .normal)
    }

    override func viewWillLayoutSubviews()
    {
        super.viewWillLayoutSubviews()
        if !self.layerAdded
        {
            self.layerAdded = true
            self.button.addLayer()
        }
    }
}

你有两个问题:

  1. 您实际上并没有设置按钮的图像。您尝试为 buttonImage 创建一个 didSet 观察者,但该观察者不会在初始化时被调用。如果 buttonImage 被重新分配,它只会在以后被调用。解决此问题的一种方法是在 setupLayout() 中将其设置为自身,如下所示:

    private func setupLayout() {
        buttonImage = { buttonImage }()
    

    请注意,Swift 不会让您将 属性 分配给它自己,因此我使用匿名闭包来获取当前的 buttonImage 值。

  2. 如果要确保您的层位于其他层之下,则​​需要在添加自己的层之前让按钮执行其正常布局。所以不要setupLayout中添加图层。等到 layoutSubviews 插入图层:

    override func layoutSubviews() {
        super.layoutSubviews()
        if pulsatingLayer.superlayer == nil {
            layer.insertSublayer(pulsatingLayer, at: 0)
            layer.insertSublayer(buttonFillLayer, at: 1)
        }
        updatePaths()
    }
    

结果: