UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Creating views in a loop

Paul Hudson    @twostraws   

It’s common to want to create several SwiftUI views inside a loop. For example, we might want to loop over an array of names and have each one be a text view, or loop over an array of menu items and have each one be shown as an image.

SwiftUI gives us a dedicated view type for this purpose, called ForEach. This can loop over arrays and ranges, creating as many views as needed.

ForEach will run a closure once for every item it loops over, passing in the current loop item. For example, if we looped from 0 to 100 it would pass in 0, then 1, then 2, and so on.

For example, this creates a form with 100 rows:

Form {
    ForEach(0..<100) { number in
        Text("Row \(number)")
    }
}

Because ForEach passes in a closure, we can use shorthand syntax for the parameter name, like this:

Form {
    ForEach(0 ..< 100) {
        Text("Row \($0)")
    }
}

ForEach is particularly useful when working with SwiftUI’s Picker view, which lets us show various options for users to select from.

To demonstrate this, we’re going to define a view that:

  1. Has an array of possible student names.
  2. Has an @State property storing the currently selected student.
  3. Creates a Picker view asking users to select their favorite, using a two-way binding to the @State property.
  4. Uses ForEach to loop over all possible student names, turning them into a text view.

Here’s the code for that:

struct ContentView: View {
    let students = ["Harry", "Hermione", "Ron"]
    @State private var selectedStudent = "Harry"

    var body: some View {
        NavigationStack {
            Form {
                Picker("Select your student", selection: $selectedStudent) {
                    ForEach(students, id: \.self) {
                        Text($0)
                    }
                }
            }
        }
    }
}

There’s not a lot of code in there, but it’s worth clarifying a few things:

  1. The students array doesn’t need to be marked with @State because it’s a constant; it isn’t going to change.
  2. The selectedStudent property starts with the value “Harry” but can change, which is why it’s marked with @State.
  3. The Picker has a label, “Select your student”, which tells users what it does and also provides something descriptive for screen readers to read aloud.
  4. The Picker has a two-way binding to selectedStudent, which means it will start showing a selection of “Harry” but update the property when the user selects something else.
  5. Inside the ForEach we loop over all the students.
  6. For each student we create one text view, showing that student’s name.

The only confusing part in there is this: ForEach(students, id: \.self). That loops over the students array so we can create a text view for each one, but the id: \.self part is important. This exists because SwiftUI needs to be able to identify every view on the screen uniquely, so it can detect when things change.

For example, if we rearranged our array so that Ron came first, SwiftUI would move its text view at the same time. So, we need to tell SwiftUI how it can identify each item in our string array uniquely – what about each string makes it unique?

If we had an array of structs we might say “oh, my struct has a title string that is always unique,” or “my struct has an id integer that is always unique.” Here, though, we just have an array of simple strings, and the only thing unique about the string is the string itself: each string in our array is different, so the strings are naturally unique.

So, when we’re using ForEach to create many views and SwiftUI asks us what identifier makes each item in our string array unique, our answer is \.self, which means “the strings themselves are unique.” This does of course mean that if you added duplicate strings to the students array you might hit problems, but here it’s just fine.

Anyway, we’ll look at other ways to use ForEach in the future, but that’s enough for this project.

This is the final part of the overview for this project, so it’s almost time to get started with the real code. If you want to save the examples you’ve programmed you should copy your project directory somewhere else.

When you’re ready, put ContentView.swift back to the code below, so we have a clean slate to work from:

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello, world!")
    }
}

#Preview {
    ContentView()
}
Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.8/5

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.