How to render text with a color gradient in SwiftUI

⋅ 4 min read ⋅ SwiftUI Gradient Text

Table of Contents

We have learned in a previous article how easy it is to create a gradient color view in SwiftUI. This article will explore how to render text with color gradients.

We want to replicate the following text style. Here is an example from https://www.apple.com/apple-card.

Color gradient text.
Color gradient text.

There are two ways to do this.

  1. Gradient using a mask (Pre iOS 15).
  2. Gradient using foregroundStyle modifier (iOS 15).

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Gradient using a mask (Pre iOS 15)

The first method is straightforward. It replicates how we normally do it in a design tool.

First, we create a gradient with the colors that we want.

struct ContentView: View {
var body: some View {
LinearGradient(
colors: [.red, .blue, .green, .yellow],
startPoint: .leading,
endPoint: .trailing
)
}
}
Linear gradient.
Linear gradient.

Then we apply .mask to it. Mask will apply the alpha channel of the given view to the masked view. In other words, we only see our gradient view where the text (mask) is rendered.

LinearGradient(
colors: [.red, .blue, .green, .yellow],
startPoint: .leading,
endPoint: .trailing
)
.mask(
// 1
Text("The simplicity of Apple.\nIn a credit card.")
.font(Font.system(size: 46, weight: .bold))
.multilineTextAlignment(.center)
)

1 Alpha channel of text is applied to the linear gradient. So, only a portion of the gradient view that sits behind the text is visible.

Using a mask to produce color gradient effect.
Using a mask to produce color gradient effect.

The problem

Render gradient text this way has one problem. The color gradient doesn't share the same starting and ending points with text. As a result, you will barely see red and yellow colors in our text since it is located on the leading and trailing edge of the screen.

The color gradient doesn't share the same starting and ending points with text.
The color gradient doesn't share the same starting and ending points with text.

If this is the behavior you want, you can use a mask to produce a gradient effect. But if you or your designer wants gradient color to start and end at the bounds of the text, you need more work to do.

Solution

We need to make sure our mask uses the same bounds as our text view to solve this. This is a perfect use case for .overlay, a modifier that will layer the views that you specify in front of this modified view.

We use .overlay to put LinearGradient on top of our text view.

Text("The simplicity of Apple.\nIn a credit card.")
.font(Font.system(size: 46, weight: .bold))
.multilineTextAlignment(.center)
.overlay {
LinearGradient(
colors: [.red, .blue, .green, .yellow],
startPoint: .leading,
endPoint: .trailing
)
}

This will produce a linear gradient that shares the same starting and ending point with the text.

The overlay modifier will layer the linear gradient in front of the text view.
The overlay modifier will layer the linear gradient in front of the text view.

Now that we can make our gradient color start and end along with the text, we need to repeat what we did in the [previous section](#gradient-with-mask-(pre-ios-15). We need to apply a mask over a linear gradient.

Text("The simplicity of Apple.\nIn a credit card.")
.font(Font.system(size: 46, weight: .bold))
.multilineTextAlignment(.center)
.overlay {
LinearGradient(
colors: [.red, .blue, .green, .yellow],
startPoint: .leading,
endPoint: .trailing
)
.mask(
Text("The simplicity of Apple.\nIn a credit card.")
.font(Font.system(size: 46, weight: .bold))
.multilineTextAlignment(.center)
)
}

We can see red and yellow colors with this method.

The color gradient share the same starting and ending points with text.
The color gradient share the same starting and ending points with text.

Improvement

As you can see, we need to repeat our text view two times. We create one to set a bound for a gradient and one as a mask.

This is hard to use, but we can make it more usable with the help of a view modifier.

Since text view already has foregroundColor(_:), I will name our new modifier foregroundLinearGradient.

extension Text {
public func foregroundLinearGradient(
colors: [Color],
startPoint: UnitPoint,
endPoint: UnitPoint) -> some View
{
self.overlay {
LinearGradient(
colors: colors,
startPoint: startPoint,
endPoint: endPoint
)
.mask(
self
)
}
}
}

This modifier is the same implementation as before. We still need two text views as depicted by two self.

Creating a view modifier makes it easier to use. We can use it like this.

Text("The simplicity of Apple.\nIn a credit card.")
.font(Font.system(size: 46, weight: .bold))
.multilineTextAlignment(.center)
.foregroundLinearGradient(
colors: [.red, .blue, .green, .yellow],
startPoint: .leading,
endPoint: .trailing
)

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Gradient using foregroundStyle (iOS 15)

If you only focus on iOS 15, you can skip all the hard work we do so far and use the new modifier, foregroundStyle.

Foreground style will set a view's foreground elements to use a given style. You can use any style that conforms to the ShapeStyle protocol as a view's foreground. Luckily LinearGradient is also conforming to ShapeStyle. So, we can pass it as an argument for .foregroundStyle.

Text("The simplicity of Apple.\nIn a credit card.")
.font(Font.system(size: 46, weight: .bold))
.multilineTextAlignment(.center)
.foregroundStyle(
LinearGradient(
colors: [.red, .blue, .green, .yellow],
startPoint: .leading,
endPoint: .trailing
)
)

The result is exactly the same as our previous method.

Color gradient using foregroundStyle.
Color gradient using foregroundStyle.

Read more article about SwiftUI, Gradient, Text, or see all available topic

Enjoy the read?

If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.

Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron Buy me a coffee Tweet Share
Previous
How to parse ISO 8601 date in Swift

Learn how to parse ISO 8601 date with ISO8601DateFormatter.

Next
Review Github PR with a web-based editor

If you prefer to review pull requests with an editor but hate the clone or checkout process, you can enjoy the review process without the hassle with new github.dev web-based editor.

← Home