Вы не хотите полагаться на 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
, чтобы я могиспользуйте его непосредственно в Интерфейсном Разработчике.Я также сделал цвета @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)
}
}
Это достигается так же, как в первом примере, но потенциально немного более эффективно.