在三个按钮之间同时添加/删除 GradientLayer

Posted

技术标签:

【中文标题】在三个按钮之间同时添加/删除 GradientLayer【英文标题】:Simultaneously Adding / Removing GradientLayer among Three Buttons 【发布时间】: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 次

如何解决这个问题?

【问题讨论】:

【参考方案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() 代码没有什么不同。

以上是关于在三个按钮之间同时添加/删除 GradientLayer的主要内容,如果未能解决你的问题,请参考以下文章

两个工具按钮和 hboxlayout 之间的空间

如何在 UIAlertView(iOS)中的其他两个按钮(堆叠)之间添加取消按钮

删除按钮和行之间不需要的空间 swift xcode

添加、删除子视图并在子视图和主 uiviewcontroller 之间进行通信

如何让按钮一次添加一个视图?

一个按钮在一个表单中怎么提交两个方法,一个添加,一个删除