【问题标题】:Why is the linear gradient not fully applied to ios button为什么线性渐变没有完全应用于ios按钮
【发布时间】:2021-06-16 07:33:54
【问题描述】:

我正在使用以下代码将线性渐变应用到 iOs 按钮

 private func applyGradient(colors: [CGColor])
{
    let gradientLayer = CAGradientLayer()
    gradientLayer.colors = colors
    gradientLayer.startPoint = CGPoint(x: 0, y: 1)
    gradientLayer.endPoint = CGPoint(x: 0, y: 0)
    gradientLayer.frame = self.addToCart.bounds 
    self.addToCart.layer.insertSublayer(gradientLayer, at: 0)
}

但是渐变没有完全应用到按钮上。这是ios按钮的图像

【问题讨论】:

  • 这是 Swift、Objective-C 还是 React-Native 和 Expo?
  • 这很快
  • 它对我不起作用
  • 正是我所做的谢谢。你想把它写成答案,我可以打勾

标签: ios button linear-gradients


【解决方案1】:

手动更新渐变层的frame 的替代方法是将按钮的layerClass 声明为CAGradientLayer

@IBDesignable
public class GradientButton: UIButton {
    public override class var layerClass: AnyClass         { CAGradientLayer.self }
    private var gradientLayer: CAGradientLayer             { layer as! CAGradientLayer }

    @IBInspectable public var startColor: UIColor = .white { didSet { updateColors() } }
    @IBInspectable public var endColor: UIColor = .red     { didSet { updateColors() } }

    // init methods

    public override init(frame: CGRect = .zero) {
        super.init(frame: frame)
        updateColors()
    }

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

private extension GradientButton {
    func updateColors() {
        gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
    }
}

这实现了建议的解决方案的行为,而且:

  • 与在视图控制器的viewDidLayoutSubviews(或者,更好的是按钮的layoutSubviews)中手动设置渐变层的frame不同,这将更优雅地响应动画布局的变化。如果按钮frame 的动画正在进行中(例如旋转设备或你有什么),这种layerClass 方法将在动画中间正确渲染按钮。渐变层的frame手动设置不会。

  • 这不是必须的,但我已经做了这个@IBDesignable,所以如果你在IB中添加这个按钮,你可以在Interface Builder中调整颜色和startPoint/endPoint,看到它是实时渲染的。

    在这里,我制作了渐变对角线,更改了颜色,并添加了一个边框,所有这些都来自 Interface Builder,使用了上述的扩展再现。

    @IBDesignable
    public class GradientButton: UIButton {
        public override class var layerClass: AnyClass         { CAGradientLayer.self }
        private var gradientLayer: CAGradientLayer             { layer as! CAGradientLayer }
    
        @IBInspectable public var startColor: UIColor = .white { didSet { updateColors() } }
        @IBInspectable public var endColor: UIColor = .red     { didSet { updateColors() } }
    
        // expose startPoint and endPoint to IB
    
        @IBInspectable public var startPoint: CGPoint {
            get { gradientLayer.startPoint }
            set { gradientLayer.startPoint = newValue }
        }
    
        @IBInspectable public var endPoint: CGPoint {
            get { gradientLayer.endPoint }
            set { gradientLayer.endPoint = newValue }
        }
    
        // while we're at it, let's expose a few more layer properties so we can easily adjust them in IB
    
        @IBInspectable public var cornerRadius: CGFloat {
            get { layer.cornerRadius }
            set { layer.cornerRadius = newValue }
        }
    
        @IBInspectable public var borderWidth: CGFloat {
            get { layer.borderWidth }
            set { layer.borderWidth = newValue }
        }
    
        @IBInspectable public var borderColor: UIColor? {
            get { layer.borderColor.flatMap { UIColor(cgColor: $0) } }
            set { layer.borderColor = newValue?.cgColor }
        }
    
        // init methods
    
        public override init(frame: CGRect = .zero) {
            super.init(frame: frame)
            updateColors()
        }
    
        required init?(coder: NSCoder) {
            super.init(coder: coder)
            updateColors()
        }
    }
    
    private extension GradientButton {
        func updateColors() {
            gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
        }
    }
    

【讨论】:

    【解决方案2】:

    通常,viewDidLoad 用于视图控制器或init 用于自定义视图对于渐变帧来说太早了。此外,它们的框架很可能稍后会发生变化,您需要处理。

    如果您将渐变应用到自定义视图,请尝试在 layoutSubviews() 内更新其框架(来自这个伟大的 answer)。

    class GradientButton: UIButton {
    
        /// update inside layoutSubviews
        override func layoutSubviews() {
            super.layoutSubviews()
            gradientLayer.frame = bounds
        }
    
        private lazy var gradientLayer: CAGradientLayer = {
            let l = CAGradientLayer()
            l.frame = self.bounds
            l.colors = [UIColor.systemYellow.cgColor, UIColor.systemPink.cgColor]
            l.startPoint = CGPoint(x: 0, y: 1)
            l.endPoint = CGPoint(x: 0, y: )
            layer.insertSublayer(l, at: 0)
            return l
        }()
    }
    

    对于视图控制器中的视图,请尝试viewDidLayoutSubviews()

    class ViewController: UIViewController {
    
        @IBOutlet weak var addToCart: UIButton!
        var gradientLayer: CAGradientLayer? /// keep a reference to the gradient layer so we can update its frame later  
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            /// still first make the gradient inside viewDidLoad
            applyGradient(colors: [UIColor.systemYellow.cgColor, UIColor.systemPink.cgColor])
        }
        
        override func viewDidLayoutSubviews() {
            super.viewDidLayoutSubviews()
    
            /// update here!
            self.gradientLayer?.frame = self.addToCart.bounds 
        }  
    
        private func applyGradient(colors: [CGColor]) {
            let gradientLayer = CAGradientLayer()
            gradientLayer.colors = colors
            gradientLayer.startPoint = CGPoint(x: 0, y: 1)
            gradientLayer.endPoint = CGPoint(x: 0, y: 0)
            gradientLayer.frame = self.addToCart.bounds 
            self.addToCart.layer.insertSublayer(gradientLayer, at: 0)
    
            self.gradientLayer = gradientLayer
        }
    }
    

    【讨论】:

    • 如果你有一个带有渐变的按钮子类,你可能应该只更新这个子类的layoutSubviews中的渐变frame,而不是视图控制器的viewDidLayoutSubviews
    • @Rob 是的,好点。也为你的答案投票
    【解决方案3】:

    尝试替换这两行:

        gradientLayer.startPoint = CGPoint(x: 0, y: 1)
        gradientLayer.endPoint = CGPoint(x: 0, y: 0)
    

    就这一行:

    gradientLayer.locations = [0.0, 1.0]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-22
      • 1970-01-01
      • 2021-12-13
      • 2021-04-10
      • 2021-09-14
      相关资源
      最近更新 更多