【问题标题】:Simultaneously Adding / Removing GradientLayer among Three Buttons在三个按钮之间同时添加/删除 GradientLayer
【发布时间】:2017-05-05 10:46:28
【问题描述】:

让我们说我有三个按钮,功能是选择按钮时,我不想为该按钮添加mygradientLayer,并希望其他两个按钮..

为了添加和删除 GradientLayer,我为 UIView 创建了一个扩展,如下所示:

extension UIView {

    private var gradientLayer : CAGradientLayer {

        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = self.bounds
        gradientLayer.colors = arrButtonColors.map({$0.cgColor})
        return gradientLayer
    }

    open func applyGradientColor() {
        self.layer.insertSublayer(gradientLayer, at: 0)
    }

    open func removeGradientLayer() {

       gradientLayer.removeFromSuperlayer() --// this does't remove gradientLayer at all

       self.layer.sublayers?.removeAll() --// this will remove all the layer include UILabelLayer , that doesn't i want

      self.layer.sublayers = nil --// same effect as above(2nd condition)

    }

}

我在 ViewController 中的功能代码:

if view.isHidden {
            btn.applyGradientColor()
        } else  {
            btn.removeGradientLayer()
        }

我知道如果 btn(Button) 没有任何层(不包括 UILabelLayer),那么它将 否则添加 GradientLayer ,因为我试图删除所有 首先子层,甚至尝试删除 view.isHidden 块中 0 索引处的子层。 尽管它也不起作用。

功能代码将针对 UIButton 的不同对象执行 3 次

如何解决这个问题?

【问题讨论】:

    标签: ios swift cagradientlayer


    【解决方案1】:

    您没有引用之前添加的gradientLayer。你必须保留它。一种方法是对其进行子类化并在其中保留变量,或者您也可以在运行时使用objc_setAssociatedObject 扩展保留它。

    以下是您可以如何执行此操作的示例:

    private var key = "gradientLayerKey"
    extension UIView {
        private var gradientLayer: CAGradientLayer? {
            get {
                return objc_getAssociatedObject(self, &key) as? CAGradientLayer
            }
    
            set {
                objc_setAssociatedObject(self, &key, newValue, .OBJC_ASSOCIATION_RETAIN)
            }
        }
    
        private var gradientLayerInstance: CAGradientLayer {
            let newGradientLayer = CAGradientLayer()
            newGradientLayer.frame = self.bounds
            newGradientLayer.colors = arrButtonColors.map { $0.cgColor }
            return newGradientLayer
        }
    
        open func applyGradientColor() {
            gradientLayer?.removeFromSuperLayer()
            // next line sets gradientLayer and retains it.
            gradientLayer = gradientLayerInstance
            self.layer.insertSublayer(gradientLayer, at: 0)
        }
    
        open func removeGradientLayer() {
            gradientLayer?.removeFromSuperlayer()
        }
    }
    

    【讨论】:

    • nice farzadshbfn,我尝试了您的代码及其为我工作,很高兴今天知道新概念,例如 objc_setAssociatedObject。谢谢
    • 那么为什么不将gradientLayer 设置为公开?我将其设置为私有以遵循您的代码,但如果您不将其标记为私有并且您可以从任何地方访问它。要么那个,要么我不太明白你的问题:D
    • @farzadshbfn,您正在使用自定义 getter/setter 和关联对象来模拟存储属性,这是另一种选择。您不能将存储的属性添加到扩展中(除非您使用答案中的关联对象。)
    • @DuncanC 这是在扩展中半存储属性的唯一方法。它只适用于类。有时它们会派上用场,尤其是在使用库时。
    【解决方案2】:

    您已将gradientLayer 设为计算属性。这意味着每次读取属性时都会获得一个新层。改为使其成为常规属性。

    这可能意味着您必须使用 UIButton 的子类,而不是扩展。或者,让您的移除代码循环遍历所有子层,并且只移除渐变层。

    编辑:

    查找和删除渐变层的代码可能如下所示:

    open func removeGradientLayer() {
       if let layers = layer.subLayers {
         for aLayer in layers {
           if aLayer is CAGradientLayer {
             aLayer.removeFromSuperlayer
             break
           }
       }
    }
    

    【讨论】:

    • 嘿 Duncan 首先感谢您的回复,但是还有其他方法可以创建 UIButton 的子类吗?为什么不用扩展?
    • 您不能在扩展中创建存储属性,并且您需要一些方法来获取渐变层才能将其删除。如果您将 UIButton 子类化,则可以使用存储的属性来删除它。正如我所提到的,另一种选择是遍历子层并删除您找到的第一个渐变层。
    • 是的,我知道我们不能在扩展中创建存储属性,这就是我创建私有计算属性的原因,但没关系 Duncan,我试过你的代码,它的工作,但只适用于我的第二个按钮三个按钮..!这意味着它找到了一个仅用于第二个按钮的 CAGradientLayer
    • 我试过这个:self.layer.sublayers?.forEach({$0.removeFromSuperlayer()}) 但这会删除 UILabelLayer
    • 好的,你需要调试你的代码来弄清楚为什么你的其他按钮没有找到它们的渐变层。 sublayers?.forEach() 代码将始终删除每一层,因此它与您的原始 sublayers?.removeAll() 代码没有什么不同。
    猜你喜欢
    • 2021-09-12
    • 1970-01-01
    • 2019-06-07
    • 1970-01-01
    • 1970-01-01
    • 2013-07-12
    • 1970-01-01
    • 2018-01-27
    • 1970-01-01
    相关资源
    最近更新 更多