Adding Text in SwiftUI | SwiftUI Bootcamp #2
Text is the most fundamental building block in any SwiftUI screen — almost every app needs labels, headings, body copy, and captions. After this lesson you'll be able to style text precisely and make it behave correctly at any Dynamic Type size or container width.
What You'll Learn
- The most useful
Textmodifiers: font, weight, color, alignment, kerning, and baseline - How
minimumScaleFactorprevents text from being cut off in narrow containers - Why commented-out modifier lines are a valuable learning tool for exploring text options
Mental Model
Think of a Text view like a piece of paper with writing on it. The text itself is the ink. Modifiers are like editing tools you apply one at a time: first you choose the pen (.font), then the thickness (.fontWeight), then the color (.foregroundColor). The paper's size on screen is controlled by .frame, and if the ink is too big for the paper, .minimumScaleFactor shrinks the ink until it fits rather than letting words disappear off the edge.
The key insight is that modifiers are applied in order and each one wraps the previous result. Reading a modifier chain top-to-bottom tells you the complete styling history of that text.
Detailed Explanation
Text in SwiftUI is far more powerful than it looks. It supports full system font styles (.largeTitle, .title, .headline, .body, .caption, etc.) that automatically respect the user's selected Dynamic Type size — users who increase font size in Settings see your text grow without any code changes. Alternatively, .font(.system(size:weight:design:)) gives you pixel-level control at the cost of that adaptability.
Typography modifiers like .kerning (space between individual characters) and .baselineOffset (vertical shift relative to the baseline) are less common but critical for polished design work. .strikethrough and .underline accept an optional color parameter so they can be styled independently from the text color.
The .frame(width:height:alignment:) modifier constrains the text's layout box, and .multilineTextAlignment controls how lines within that box are aligned. Note that .multilineTextAlignment only takes effect when text actually wraps — it has no visible effect on a single line.
.minimumScaleFactor is a safety net: if the text can't fit at its specified font size within the frame, SwiftUI scales it down proportionally until it reaches the minimum factor you specify (e.g., 0.1 means it can shrink to 10% of the original size). Without this, text simply gets clipped.
Code Structure
The sample is in TextBootcamp.swift and contains a single TextBootcamp view. Most of the interesting modifiers are commented out — this is intentional. Uncomment them one at a time to see exactly what each one does in isolation, which is a faster way to learn than reading documentation alone.
Complete Code
TextBootcamp.swift
import SwiftUI
struct TextBootcamp: View {
var body: some View {
Text("Hello, World!".capitalized)
//.font(.body)
//.fontWeight(.semibold)
//.bold()
//.underline()
// .underline(true, color: Color.red) // underline with a custom color separate from the text
// .italic()
// .strikethrough(true, color: Color.green) // strikethrough with its own color
//.font(.system(size: 24, weight: .semibold, design: .serif)) // custom size + serif design
//.baselineOffset(-50.0) // shifts the text downward relative to its baseline
//.kerning(10) // adds 10 points of space between each character
.multilineTextAlignment(.leading) // left-aligns wrapped text within the frame
.foregroundColor(.red) // sets the text color to red
.frame(width: 200, height: 100, alignment: .leading) // constrains the layout box
.minimumScaleFactor(0.1) // shrinks text down to 10% if it can't fit at full size
}
}
struct TextBootcamp_Previews: PreviewProvider {
static var previews: some View {
TextBootcamp()
}
}Code Walkthrough
"Hello, World!".capitalized— The.capitalizedproperty onStringcapitalizes the first letter of each word. This happens at the Swift level before SwiftUI even sees the string, so it's not a SwiftUI modifier.- Commented modifier block — The commented lines are an exploration menu. Each demonstrates a distinct text style. Uncomment one at a time — for example, try
.kerning(10)to dramatically widen character spacing and see how it changes the visual rhythm of the text. .multilineTextAlignment(.leading)— This only matters when the text wraps to multiple lines inside the frame..leadingis left-aligned in LTR languages. Use.centerfor titles that should center-wrap..foregroundColor(.red)— Sets the color. Notice this is applied after the alignment modifier — in SwiftUI, the order of modifiers that operate on different aspects of the view (layout vs. color) often doesn't matter, but there are exceptions. Layout modifiers like.frameshould generally come before decoration modifiers like.shadow..frame(width: 200, height: 100, alignment: .leading)— Creates a fixed 200×100 point box. Thealignmentparameter positions theTextinside the frame — since the text may be smaller than the box, this controls where it sits within that space..minimumScaleFactor(0.1)— Acts as a shrink-to-fit safety valve. If the text can't fit at 100% within the frame, SwiftUI reduces the font size in steps until it fits or hits the 10% floor.
Common Mistakes
Mistake: Using .multilineTextAlignment and wondering why it has no effect on a single line.multilineTextAlignment only changes the alignment of individual lines when the text wraps. If your text fits on one line, the modifier is silently ignored. To position a single-line text label, use the alignment parameter on .frame() or the alignment parameter on the parent stack.
Mistake: Setting .font(.system(size: 17)) instead of .font(.body) for body copy Hard-coded sizes don't respond to Dynamic Type. A user who has set their phone to large text will see your hard-coded 17pt text stay small while all system text around it grows. Use semantic styles (.body, .headline, etc.) for any text users are expected to read.
Mistake: Chaining .bold() after .font(.system(size:weight:)) expecting it to override When you set a specific weight inside .font(.system(..., weight: .regular)) and then call .bold(), the behavior can be surprising. Prefer setting weight in one place — either inside the .font modifier or via .fontWeight(), not both.
Key Takeaways
- Use semantic font styles (
.title,.body,.caption) instead of point sizes to support Dynamic Type automatically .multilineTextAlignmentonly applies when text actually wraps — use.frame(alignment:)for single-line positioning.minimumScaleFactoris essential for any text displayed in a constrained or unknown-width container
Last updated: June 27, 2026