Is there any way of using a gradient as foregroundColor of Text in SwiftUI?
Thanks for the answers in advance!
I would say it isn't possible in pure SwiftUI. You need to use a modified UILabel
and wrap it via UIViewRepresentable
to use it in SwiftUI.
I scraped code from previous answers together to make a basic example for a horizontal gradient:
class GradientLabel: UILabel {
var gradientColors: [CGColor] = []
override func drawText(in rect: CGRect) {
if let gradientColor = drawGradientColor(in: rect, colors: gradientColors) {
self.textColor = gradientColor
}
super.drawText(in: rect)
}
private func drawGradientColor(in rect: CGRect, colors: [CGColor]) -> UIColor? {
let currentContext = UIGraphicsGetCurrentContext()
currentContext?.saveGState()
defer { currentContext?.restoreGState() }
let size = rect.size
UIGraphicsBeginImageContextWithOptions(size, false, 0)
guard let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(),
colors: colors as CFArray,
locations: nil) else { return nil }
let context = UIGraphicsGetCurrentContext()
context?.drawLinearGradient(gradient,
start: CGPoint.zero,
end: CGPoint(x: size.width, y: 0),
options: [])
let gradientImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let image = gradientImage else { return nil }
return UIColor(patternImage: image)
}
}
struct MyTextView: UIViewRepresentable {
var width: CGFloat
func makeUIView(context: Context) -> GradientLabel {
let label = GradientLabel()
label.gradientColors = [UIColor.blue.cgColor, UIColor.red.cgColor]
label.lineBreakMode = .byWordWrapping
label.numberOfLines = 0
label.preferredMaxLayoutWidth = width
label.text = "Here's a lot of text for you to display. It won't fit on the screen."
return label
}
func updateUIView(_ view: GradientLabel, context: Context) {
}
}
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
MyTextView(width: geometry.size.width)
}
}
}
Reference to the GradientLabel class
Reference to wrapping a UILabel to use in SwiftUI
I hope this helps!