The Text
view is an essential part of the SwiftUI framework. It’s hard to imagine a clearer representation of data than plain text. Simple at first glance, the most complex tasks are often the ones with the shortest descriptions. That’s why even in a live-coding interview, the Text
view can shine from a new angle. "Shining" is not an abstract term here.
Recently, one such session I was asked a specific feature:
Given a paragraph of solid text. Need to add a gradient to the first word and then animate it. Imagine, no Claude, ChatGPT, Cursor available.
Sounds like a challenge and an interesting one. Let’s start from the basic styling.
Available Styling
We have a modest but satisfying list of view modifiers for Text
:
font
/fontWeight
/fontDesign
/fontWidth
bold
/italic
/strikethrough
/underline
monospaced
/monospacedDigit
kerning
/tracking
/baselineOffset
And our old friend from the start,
foregroundColor
ForegroundColor
Capable to work starting from iOS 13 till iOS 26. Here is a short-list of main feature:
Purpose: Applies a solid
Color
to the view's foreground, such as the text color inText
views. This is the classic, straightforward way to change text color.Deprecation Status: In modern SwiftUI versions,
foregroundColor(_:)
has become outdated. The recommended replacement is the more flexibleforegroundStyle(_:)
, which supports not only solid colors but also gradients, materials, and hierarchical styling.Limitations: accepts only
Color
struct
ForegroundStyle
Was introduced in SwiftUI for iOS 15, macOS 12, tvOS 15, and watchOS 8 (WWDC 2021) and now accepts a ShapeStyle. This brings a much wider styling abilities than before.
Multiple structures conforms to ShapeStyle. The list is interesting and provides an interesting effects now.
Solid Colors
Color
Materials (iOS 15+, macOS 12+)
Material
Predefined materials like
.ultraThinMaterial
,.thinMaterial
,.regularMaterial
,.thickMaterial
,.ultraThickMaterial
.
Gradients
LinearGradient
RadialGradient
AngularGradient
EllipticalGradient
(iOS 17+, macOS 14+)
Assume that we have:
let rainbowColors = [Color.red, Color.orange, Color.yellow, Color.green, Color.blue, Color.indigo, Color.purple]
Image-Based
ImagePaint
This a cool shape style that fills a shape by repeating a region of an image.
I’ve found this nice image:
And set it with some scale because we need to either set a source rect of an image or set the scale:
Text("Sample App")
.foregroundStyle(ImagePaint(image: Image("rainbow"), scale: 0.5))
Hierarchical Styles (iOS 15+, macOS 12+)
HierarchicalShapeStyle
(e.g..primary
,.secondary
,.tertiary
,.quaternary
)
Let’s embed some Texts
into HStack
:
HStack {
Text("Sample App")
.foregroundStyle(.primary)
Text("Sample App")
.foregroundStyle(.secondary)
Text("Sample App")
.foregroundStyle(.tertiary)
}
.foregroundStyle(.cyan)
Parent style sets the main content style and child styles are applying the levels.
Separation/Foreground/Background Styles
SeparatorShapeStyle
ForegroundStyle
BackgroundStyle
There is another way to set a color: AttributedString and we will highlight in below.
Concatenation
Now it’s time to merge texts! In real-life apps, we often work with solid paragraphs and multiline text. However, what if we need to merge multiple sources but show them as a single piece? Let’s dive into possible solutions.
Text + Text
+(_:_:) operator was a first solution till deprecation in iOS 26. Still working and has a small caveat.
Text("Sample ").foregroundStyle(.red)
+ Text("App").foregroundStyle(.green)
The problem is that the left and right elements must be of type Text
. As soon as we apply a modifier that erases the type (like many non-Text
modifiers), an error will appear:
Cannot convert value of type 'some View' to expected argument type 'Text'
This mean that staged animation is not possible with this approach.
Attributed Strings
We can declare 2 Attributed String with different colors:
var message1: AttributedString {
var result = AttributedString("Sample ")
result.foregroundColor = .red
return result
}
var message2: AttributedString {
var result = AttributedString("App")
result.foregroundColor = .green
return result
}
Text(message1 + message2)
Hm… What about gradient? AttributedString
accepts solid colors only. Gradient can be added to the whole View (
Text(message1 + message2)
.overlay(
LinearGradient(
colors: rainbowColors,
startPoint: .leading,
endPoint: .trailing
)
)
.mask(Text(message1 + message2))
This is great but this is not what we want exactly. Let’s get close to the original assessment now.
Since +(_:_:) (I really like how it looks) operator is deprecated, Apple suggested a new way to join texts. Let’s open the warning:
Deprecated
Use string interpolation on `Text` instead: `Text(\"Hello \(name)\")`
Alright, let’s do it as suggested.
Text("Sample \(Text("app with a huge\nfeatures amount").foregroundStyle(.black))")
.foregroundStyle(
LinearGradient(
colors: rainbowColors,
startPoint: .leading,
endPoint: .trailing
)
)
Amazing, isn’t it? Now let’s add animation. Fortunately, the string interpolation init allows us to implement it.
Text("Sample \(Text("app with a huge\nfeatures amount").foregroundStyle(.black))")
.phaseAnimator(0...rainbowColors.count)
{ view, phase in
view.foregroundStyle(
LinearGradient(
colors: shiftedColors(rainbowColors,
by: phase),
startPoint: .leading,
endPoint: .trailing
)
)
}
If you want to tweak the animation - then simply add to phaseAnimator
:
animation: { phase in
.linear(duration: 2.0)
}
All done, and we can mark it as complete ✅.
Great article. But I can’t get my head why I can’t use this technique with other views.
Text("hello \(Text("world").foregroundStyle(.blue)) \(Image(systemName: "diamond"))").foregroundStyle(.red) // ✅
Image(systemName: "diamond")
.foregroundStyle(.green) ✅
Text("hello \(Text("world").foregroundStyle(.blue)) \(Image(systemName: "diamond").foregroundStyle(.green))") ❌