SwiftUI Fundamentals: Views & Modifiers

iOS Development Fundamentals

Chapter 5 · SwiftUI Fundamentals: Views & Modifiers

Every piece of Chapters 1-4 was building toward this: real SwiftUI views, and the modifiers that style them. This chapter also resolves a real, common point of confusion — what a modifier actually does under the hood, and why the order they're applied in genuinely changes the result.

Core Building-Block Views

Text

Text("Hello") — displays a real string.

Image

Image(systemName: "star") — a real SF Symbol, or Image("photoName") from the asset catalog.

Button

Button("Tap Me") { } — a real, tappable trailing-closure action, per Chapter 3.

Button("Tap Me") { print("Button was tapped") }

Modifiers

Text("Hello, SwiftUI!") .font(.title) .foregroundStyle(.blue) .padding() .background(.yellow) .clipShape(.capsule)
ModifierWhat It Does
.padding()Adds real space around the view — an optional argument sets a specific amount; with none, a sensible system default.
.font(.title)Sets real text size/weight using one of SwiftUI's built-in Dynamic Type text styles.
.foregroundStyle(.blue)Sets the real foreground color — the current, general-purpose modifier, also accepting gradients and materials, not just flat colors.
.background(.yellow)Places a real background behind the view.
.clipShape(.capsule)Clips the view to a real shape — here, a capsule (a fully rounded pill).

What a Modifier Actually Does

This is the real, genuinely important mechanism underneath the syntax: a modifier doesn't change the view it's called on — it returns a brand-new view, wrapping the original one. Calling .padding() on a Text doesn't give you back a modified Text; it gives you a new, real view value whose entire job is "draw this Text, with padding added around it."

Why This Explains Modifier Order
Because each modifier wraps the view built so far — rather than editing shared, mutable properties on one object — the order modifiers are chained in changes what's actually being wrapped at each step, and therefore genuinely changes the real result:
// Padding, THEN background — the yellow extends into the padding Text("A").padding().background(.yellow) // Background, THEN padding — the yellow hugs just the text, padding is outside it Text("A").background(.yellow).padding()
A Real, Common Beginner Trap
Both lines above compile fine and look almost identical at a glance — but they render genuinely differently. When a background looks wrong, the real first thing to check is modifier order, not the color or shape values themselves.

Extracting a Subview

Once a view's own body starts getting long, real, plain Swift structs conforming to View — exactly the pattern from Chapter 1's own ContentView — are how SwiftUI code stays readable:

struct BadgeView: View { let text: String var body: some View { Text(text) .font(.caption) .padding(6) .background(.blue) .clipShape(.capsule) } } // Used just like any built-in view: BadgeView(text: "New")
A Real, Zero-Cost Pattern
Because SwiftUI views are structs (Chapter 4), extracting a piece of view code into its own named struct has no meaningful real runtime cost — it's a plain value copy, not an object allocation. There's no real reason to avoid breaking a long body into several small, named views.

Hands-On Exercises

Exercise 1

Create a Text view showing your name, styled with .font(.largeTitle), a .foregroundStyle of your choice, .padding(), and a .background — then swap the order of .padding() and .background and describe, in your own words, the real visual difference.

📄 View solution
Exercise 2

Create a struct StatusBadge conforming to View with a let label: String and a let color: Color property, rendering as a padded, colored, capsule-clipped Text. Use it twice inside another view's body with two different colors.

📄 View solution
Exercise 3

Explain, in your own words, why "a modifier returns a new, wrapped view rather than mutating the original" is exactly why chaining .padding() before versus after .background() produces two genuinely different results.

📄 View solution

Chapter 5 Quick Reference

  • Core views: Text, Image, Button
  • Common modifiers: .padding(), .font(), .foregroundStyle(), .background(), .clipShape()
  • A modifier returns a brand-new, wrapped view — it never mutates the original view value
  • Modifier order genuinely changes the result, since each one wraps whatever was built by the previous step
  • Extracting a subview is a plain, real struct conforming to View — a zero-cost pattern worth using freely