为什么线性渐变没有完全应用于 ios 按钮
Why is the linear gradient not fully applied to ios button
我正在使用以下代码将线性渐变应用于 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 按钮的图片
尝试替换这两行:
gradientLayer.startPoint = CGPoint(x: 0, y: 1)
gradientLayer.endPoint = CGPoint(x: 0, y: 0)
加上这一行:
gradientLayer.locations = [0.0, 1.0]
通常,viewDidLoad
用于视图控制器或 init
用于自定义视图对于渐变框架来说太早了。此外,他们的框架很可能稍后会发生变化,您需要处理。
如果您要将渐变应用于自定义视图,请尝试在 layoutSubviews()
中更新其框架(来自这个很棒的 )。
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
}
}
手动更新渐变图层 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]
}
}
这实现了建议解决方案的行为,而且:
不同于手动设置渐变图层的frame
在view controller的viewDidLayoutSubviews
(或者更好的是按钮的layoutSubviews
),这样会响应动画布局变化更优雅。如果按钮的 frame
动画正在进行中(例如旋转设备或您有什么),此 layerClass
方法将在动画中间正确呈现按钮。手动设置渐变层的frame
不会。
不是必须的,但是我做了这个@IBDesignable
,所以如果你在IB中添加这个按钮,你可以调整颜色和startPoint
/endPoint
就在 Interface Builder 中,看到它实时呈现。
在这里我制作了渐变对角线,更改了颜色,并添加了一个边框,所有这些都是使用上面的扩展再现从 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: [=11=]) } }
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]
}
}
我正在使用以下代码将线性渐变应用于 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 按钮的图片
尝试替换这两行:
gradientLayer.startPoint = CGPoint(x: 0, y: 1)
gradientLayer.endPoint = CGPoint(x: 0, y: 0)
加上这一行:
gradientLayer.locations = [0.0, 1.0]
通常,viewDidLoad
用于视图控制器或 init
用于自定义视图对于渐变框架来说太早了。此外,他们的框架很可能稍后会发生变化,您需要处理。
如果您要将渐变应用于自定义视图,请尝试在 layoutSubviews()
中更新其框架(来自这个很棒的
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
}
}
手动更新渐变图层 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]
}
}
这实现了建议解决方案的行为,而且:
不同于手动设置渐变图层的
frame
在view controller的viewDidLayoutSubviews
(或者更好的是按钮的layoutSubviews
),这样会响应动画布局变化更优雅。如果按钮的frame
动画正在进行中(例如旋转设备或您有什么),此layerClass
方法将在动画中间正确呈现按钮。手动设置渐变层的frame
不会。不是必须的,但是我做了这个
@IBDesignable
,所以如果你在IB中添加这个按钮,你可以调整颜色和startPoint
/endPoint
就在 Interface Builder 中,看到它实时呈现。在这里我制作了渐变对角线,更改了颜色,并添加了一个边框,所有这些都是使用上面的扩展再现从 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: [=11=]) } } 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] } }