NEW: Get your ticket for Hacking with Swift Live 2019! >>

< How to return different view types   How to create different layouts using size classes >

How to create views in a loop using ForEach

You will commonly find that you want to loop over a sequence to create views, and in SwiftUI that’s done using ForEach.

Important: It’s easy to look at ForEach and think it’s the same as the forEach() method on Swift’s sequences, but this is not the case as you’ll see.

ForEach in SwiftUI is a view struct in its own right, which means you can return it directly from your view body if you want. You provide it an array of items, and you may also need to tell SwiftUI how it how it can identify each of your items uniquely so it knows how to update them when values change. You also pass it a closure to run to create a view for each item in the loop.

For simple loops over ranges, you can pass the range directly into ForEach. For example, this counts from 10 down to 1 then adds a message at the end:

VStack(alignment: .leading) {
    ForEach((1...10).reversed()) {
        Text("\($0)…")
    }

    Text("Ready or not, here I come!")
}

For loops over arrays of simple types, such as strings, integers, colors, and so on, you can use .identified(by: \.self) on the array to have SwiftUI use the value itself as the identifier. So, if your array was ["cat", "dog", "monkey"] then SwiftUI would use those strings themselves as the identifiers for your views.

So, this code creates an array of three colors, loops over them all, and creates text views using each color name and color value:

struct ContentView : View {
    let colors: [Color] = [.red, .green, .blue]

    var body: some View {
        VStack {
            ForEach(colors.identified(by: \.self)) { color in
                Text(color.description.capitalized)
                    .padding()
                    .background(color)
            }
        }
    }
}

If you have custom types in your array, you should use identified(by:) with whatever property inside your type identifies it uniquely.

For example, here’s a struct to store test results like this:

struct Result {
    var id = UUID()
    var score: Int
}

That has an id property with a UUID, which mean it’s guaranteed to be unique – perfect for our purposes. If we wanted to loop over an array of results, creating a text view showing each result in a VStack, then we’d use this:

struct ContentView : View {
    let results = [Result(score: 8), Result(score: 5), Result(score: 10)]

    var body: some View {
        VStack {
            ForEach(results.identified(by: \.id)) { result in
                Text("Result: \(result.score)")
            }
        }
    }
}

That tells SwiftUI it can distinguish between views inside the ForEach by looking at their id property.

Tip: If you make Result conform to Identifiable protocol, you can just write ForEach(results). Conforming to this protocol means adding an id property that uniquely identifies each object, which in our case we already have, so you can just write struct Result: Identifiable {!

HACKING WITH SWIFT LIVE This July is a new two-day event where you'll be inspired by great speakers on day one then learn all the amazing new features from WWDC on day two – click here for more information and tickets.

< How to return different view types   How to create different layouts using size classes >
MASTER SWIFT NOW
Buy Testing Swift Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let me know!

Average rating: 5.0/5

Click here to visit the Hacking with Swift store >>