具有渐变文本颜色的自定义 UILabel 始终变为黑色
Custom UILabel with gradient text colour always become black
我创建了一个自定义的 UILabel 来显示 grediant 文本,但它总是将文本显示为黑色...当我在视图控制器上使用相同的代码时它可以工作!!
import UIKit
class GradientLabel: UILabel {
override func awakeFromNib() {
super.awakeFromNib()
let gredient = GradientView.init(frame: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height))
gredient.bottomColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1)
gredient.topColor = #colorLiteral(red: 0.1764705926, green: 0.01176470611, blue: 0.5607843399, alpha: 1)
setTextColorToGradient(image: imageWithView(view: gredient)!)
}
func imageWithView(view: UIView) -> UIImage? {//bet7awel uiview to image
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img
}
func setTextColorToGradient(image: UIImage) {//beta7'od image we tekteb beha el text fe el label
UIGraphicsBeginImageContext(frame.size)
image.draw(in: bounds)
let myGradient = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
textColor = UIColor(patternImage: myGradient!)
}
}
你应该 init
你的标签而不是 awakeFromNib
因为你没有为你的标签使用 xib
class GradientLabel: UILabel {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
并且扩展将是使您的代码可重用的完美选择
extension UILabel {
func setTextColorToGradient(_ image: UIImage) {
UIGraphicsBeginImageContext(frame.size)
image.draw(in: bounds)
let myGradient = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
textColor = UIColor(patternImage: myGradient!)
}
}
用法:
label.setTextColorToGradient(UIImage(named:"someImage")!)
你不想依赖 awakeFromNib
。此外,您真的不想在 init
中执行此操作,因为您希望能够响应大小变化(例如,如果您有约束并且 frame
在标签第一个之后发生变化已创建)。
相反,从 layoutSubviews
更新渐变,每当视图的 frame
发生变化时调用它:
@IBDesignable
class GradientLabel: UILabel {
@IBInspectable var topColor: UIColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1) {
didSet { setNeedsLayout() }
}
@IBInspectable var bottomColor: UIColor = #colorLiteral(red: 0.1764705926, green: 0.01176470611, blue: 0.5607843399, alpha: 1) {
didSet { setNeedsLayout() }
}
override func layoutSubviews() {
super.layoutSubviews()
updateTextColor()
}
private func updateTextColor() {
let image = UIGraphicsImageRenderer(bounds: bounds).image { _ in
let gradient = GradientView(frame: bounds)
gradient.topColor = topColor
gradient.bottomColor = bottomColor
gradient.drawHierarchy(in: bounds, afterScreenUpdates: true)
}
textColor = UIColor(patternImage: image)
}
}
这导致:
注意,
我使用了新的 UIGraphicsImageRenderer
而不是 UIGraphicsBeginImageContext
、UIGraphicsGetImageFromCurrentImageContext
和 UIGraphicsEndImageContext
.
我没有手动构建 CGRect
,而是使用 bounds
.
我做到了@IBDesignable
这样我就可以直接在Interface Builder中使用了。我还制作了颜色 @IBInspectable
,因此您可以直接在 IB 中调整颜色,而无需编写代码。很明显,如果你想看到在IB中渲染的渐变效果,你只需要这样做。
我让它在标签需要重新布局时更新渐变(a);和 (b) 每当您更改其中一种颜色时。
为了它的价值,这是 GradientView
我用于此示例的目的:
@IBDesignable
class GradientView: UIView {
override class var layerClass: AnyClass { return CAGradientLayer.self }
private var gradientLayer: CAGradientLayer { return layer as! CAGradientLayer }
@IBInspectable var topColor: UIColor = .white { didSet { updateColors() } }
@IBInspectable var bottomColor: UIColor = .blue { didSet { updateColors() } }
override init(frame: CGRect = .zero) {
super.init(frame: frame)
updateColors()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
updateColors()
}
private func updateColors() {
gradientLayer.colors = [topColor.cgColor, bottomColor.cgColor]
}
}
在这种情况下,因为我们将 layerClass
设置为渐变,我们需要做的就是在 init
期间配置它,而基础 layerClass
将采用注意响应尺寸变化。
或者,您可以使用 CoreGraphics 绘制渐变:
@IBDesignable
class GradientLabel: UILabel {
@IBInspectable var topColor: UIColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1) {
didSet { setNeedsLayout() }
}
@IBInspectable var bottomColor: UIColor = #colorLiteral(red: 0.1764705926, green: 0.01176470611, blue: 0.5607843399, alpha: 1) {
didSet { setNeedsLayout() }
}
override func layoutSubviews() {
super.layoutSubviews()
updateTextColor()
}
private func updateTextColor() {
let image = UIGraphicsImageRenderer(bounds: bounds).image { context in
let colors = [topColor.cgColor, bottomColor.cgColor]
guard let gradient = CGGradient(colorsSpace: nil, colors: colors as CFArray, locations: nil) else { return }
context.cgContext.drawLinearGradient(gradient,
start: CGPoint(x: bounds.midX, y: bounds.minY),
end: CGPoint(x: bounds.midX, y: bounds.maxY),
options: [])
}
textColor = UIColor(patternImage: image)
}
}
这实现了与第一个示例相同的效果,但效率可能更高。
我创建了一个自定义的 UILabel 来显示 grediant 文本,但它总是将文本显示为黑色...当我在视图控制器上使用相同的代码时它可以工作!!
import UIKit
class GradientLabel: UILabel {
override func awakeFromNib() {
super.awakeFromNib()
let gredient = GradientView.init(frame: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height))
gredient.bottomColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1)
gredient.topColor = #colorLiteral(red: 0.1764705926, green: 0.01176470611, blue: 0.5607843399, alpha: 1)
setTextColorToGradient(image: imageWithView(view: gredient)!)
}
func imageWithView(view: UIView) -> UIImage? {//bet7awel uiview to image
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img
}
func setTextColorToGradient(image: UIImage) {//beta7'od image we tekteb beha el text fe el label
UIGraphicsBeginImageContext(frame.size)
image.draw(in: bounds)
let myGradient = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
textColor = UIColor(patternImage: myGradient!)
}
}
你应该 init
你的标签而不是 awakeFromNib
因为你没有为你的标签使用 xib
class GradientLabel: UILabel {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
并且扩展将是使您的代码可重用的完美选择
extension UILabel {
func setTextColorToGradient(_ image: UIImage) {
UIGraphicsBeginImageContext(frame.size)
image.draw(in: bounds)
let myGradient = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
textColor = UIColor(patternImage: myGradient!)
}
}
用法:
label.setTextColorToGradient(UIImage(named:"someImage")!)
你不想依赖 awakeFromNib
。此外,您真的不想在 init
中执行此操作,因为您希望能够响应大小变化(例如,如果您有约束并且 frame
在标签第一个之后发生变化已创建)。
相反,从 layoutSubviews
更新渐变,每当视图的 frame
发生变化时调用它:
@IBDesignable
class GradientLabel: UILabel {
@IBInspectable var topColor: UIColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1) {
didSet { setNeedsLayout() }
}
@IBInspectable var bottomColor: UIColor = #colorLiteral(red: 0.1764705926, green: 0.01176470611, blue: 0.5607843399, alpha: 1) {
didSet { setNeedsLayout() }
}
override func layoutSubviews() {
super.layoutSubviews()
updateTextColor()
}
private func updateTextColor() {
let image = UIGraphicsImageRenderer(bounds: bounds).image { _ in
let gradient = GradientView(frame: bounds)
gradient.topColor = topColor
gradient.bottomColor = bottomColor
gradient.drawHierarchy(in: bounds, afterScreenUpdates: true)
}
textColor = UIColor(patternImage: image)
}
}
这导致:
注意,
我使用了新的
UIGraphicsImageRenderer
而不是UIGraphicsBeginImageContext
、UIGraphicsGetImageFromCurrentImageContext
和UIGraphicsEndImageContext
.我没有手动构建
CGRect
,而是使用bounds
.我做到了
@IBDesignable
这样我就可以直接在Interface Builder中使用了。我还制作了颜色@IBInspectable
,因此您可以直接在 IB 中调整颜色,而无需编写代码。很明显,如果你想看到在IB中渲染的渐变效果,你只需要这样做。我让它在标签需要重新布局时更新渐变(a);和 (b) 每当您更改其中一种颜色时。
为了它的价值,这是 GradientView
我用于此示例的目的:
@IBDesignable
class GradientView: UIView {
override class var layerClass: AnyClass { return CAGradientLayer.self }
private var gradientLayer: CAGradientLayer { return layer as! CAGradientLayer }
@IBInspectable var topColor: UIColor = .white { didSet { updateColors() } }
@IBInspectable var bottomColor: UIColor = .blue { didSet { updateColors() } }
override init(frame: CGRect = .zero) {
super.init(frame: frame)
updateColors()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
updateColors()
}
private func updateColors() {
gradientLayer.colors = [topColor.cgColor, bottomColor.cgColor]
}
}
在这种情况下,因为我们将 layerClass
设置为渐变,我们需要做的就是在 init
期间配置它,而基础 layerClass
将采用注意响应尺寸变化。
或者,您可以使用 CoreGraphics 绘制渐变:
@IBDesignable
class GradientLabel: UILabel {
@IBInspectable var topColor: UIColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1) {
didSet { setNeedsLayout() }
}
@IBInspectable var bottomColor: UIColor = #colorLiteral(red: 0.1764705926, green: 0.01176470611, blue: 0.5607843399, alpha: 1) {
didSet { setNeedsLayout() }
}
override func layoutSubviews() {
super.layoutSubviews()
updateTextColor()
}
private func updateTextColor() {
let image = UIGraphicsImageRenderer(bounds: bounds).image { context in
let colors = [topColor.cgColor, bottomColor.cgColor]
guard let gradient = CGGradient(colorsSpace: nil, colors: colors as CFArray, locations: nil) else { return }
context.cgContext.drawLinearGradient(gradient,
start: CGPoint(x: bounds.midX, y: bounds.minY),
end: CGPoint(x: bounds.midX, y: bounds.maxY),
options: [])
}
textColor = UIColor(patternImage: image)
}
}
这实现了与第一个示例相同的效果,但效率可能更高。