跳转至
Structure

ForEach

Creates views from a collection of identified data.

Declaration

struct ForEach<Data, ID, Content> where Data : RandomAccessCollection, ID : Hashable

Overview

ForEach supports three identifiers:

Iterating over a range

This initializer is ForEach's most trivial. It is analogous to a common for loop.

struct ExampleView: View {
    let myFruits: [String] = ["🍌🍌", "🍑🍑", "🍎🍎"]

    var body: some View {
        VStack {
            ForEach(0..<myFruits.count) { index in
                Text(myFruits[index])
            }
        }
    }
}
A view containing a ForEach statement that houses a VStack with a text view populated by a string array, "🍌🍌", "🍑🍑", "🍎🍎", called myFruits that the enveloping ForEach statement indexes through.

Notice however, that this view is only rendered once. Therefore, if myFruits.count changes, the view will not update. For example, clicking New Fruit in the following code returns the error:

ForEach(_:content:) should only be used for constant data.

struct ErrorView: View {
    @State var myFruits: [String] = ["🍌🍌", "🍑🍑", "🍎🍎"]

    var body: some View {
        Button("New Fruit") {
            newFruit()
        }
        VStack {
            ForEach(0..<myFruits.count) { index in
                Text(myFruits[index])
            }
        }
    }

    func newFruit() {
        let allFruit: [String] = ["🍏🍏", "🍒🍒", "🍓🍓", "🥝🥝", "🥭🥭", "🍊🍊", "🍍🍍"]

        myFruits.append(allFruit.randomElement()!)
    }
}
A view containing buttn reading "NewFruit" and a ForEach statement that houses a VStack with a text view populated by a string array, "🍌🍌", "🍑🍑", "🍎🍎", called myFruits that the enveloping ForEach statement loops through; the button calls a function that appends a new fruit to the string array, resulting in an error because ForEach(_:content:) should only be used with constant data.

The instance only reads the initial value of the provided data and doesn't need to identify views across updates. To compute views on demand over a dynamic range, use init(_:id:content:).

Iterating over Identifiable data

If your data does not conform to identifiable, use init(_:id:content:).

Note: if your data does not conform to identifiable you will receive the following error:

Initializer 'init(_:rowContent:)' requires that ‘SomeType’ conform to 'Identifiable'

An array of primitive types, such as strings & ints, will throw this error. Identify these items with id: .self – because they themselves can be used as the identifiable object. See more in init(_:id:content:).

struct ExampleView: View {
    let myFruits: [Fruit] = [
        Fruit(emoji: "🍌🍌", name: "Banana"),
        Fruit(emoji: "🍑🍑", name: "Peach"),
        Fruit(emoji: "🍎🍎", name: "Apple")
    ]

    var body: some View {
        VStack {
            ForEach(myFruits) { fruit in
                Text(fruit.name + fruit.emoji)
            }
        }
    }
}

struct Fruit: Identifiable {
    var emoji: String
    var name: String
    // Create a unique ID for our object
    // This idea allows Fruit to conform to Identifiable
    let id = UUID()
}
A view containing a ForEach statement that houses a VStack with a text view populated by a Fruit array called myFruits that the enveloping ForEach statement loops through; each Fruit item has both an emoji string and a name and the text view concatenates them in reverse order, resulting in a VStack with "Banana🍌🍌", "Peach🍑🍑", "Apple🍎🍎".

Explicitly identifying data

For data that does not conform to Identifiable, use this initializer.

A very common use case for this initialier is iterating over primitive data, such as strings or ints. In the following example, the fruit string is used as the identifiable unit.

struct ExampleView: View {
    let myFruits: [String] = ["🍌🍌", "🍑🍑", "🍎🍎"]

    var body: some View {
        ForEach(myFruits, id:\.self) { fruit in
            HStack {
                Text(fruit)
            }
        }
    }
}
A view containing a ForEach statement that houses a VStack with a text view populated by a string array, "🍌🍌", "🍑🍑", "🍎🍎", called myFruits that the enveloping ForEach statement iterates through as identifiable units.

This initializer can also be used with objects that don't conform to Identifiable, but have identifiable properties. For example:

struct ExampleView: View {
    let myFruits: [Fruit] = [
        Fruit(emoji: "🍌🍌", name: "Banana"),
        Fruit(emoji: "🍑🍑", name: "Peach"),
        Fruit(emoji: "🍎🍎", name: "Apple")
    ]

    var body: some View {
        ForEach(myFruits, id: \.emoji) { fruit in
            HStack {
                Text(fruit.name + fruit.emoji)
            }
        }
    }
}

struct Fruit {
    var emoji: String
    var name: String
}
A view containing a ForEach statement that houses a VStack with a text view populated by a Fruit array called myFruits that the enveloping ForEach statement iterates through as identifiable objects; each Fruit item has both an emoji string and a name and the text view concatenates them in reverse order, resulting in a VStack with "Banana🍌🍌", "Peach🍑🍑", "Apple🍎🍎".

Notice, this initializer can be used for data that can change. For example:

struct ExampleView: View {
    @State var myFruits: [String] = ["🍌🍌", "🍑🍑", "🍎🍎"]

    var body: some View {
        Button("New Fruit") {
            newFruit()
        }

        ForEach(myFruits, id: \.self) { fruit in
            HStack {
                Text(fruit)
            }
        }
    }

    func newFruit() {
        let allFruit: [String] = ["🍏🍏", "🍒🍒", "🍓🍓", "🥝🥝", "🥭🥭", "🍊🍊", "🍍🍍"]

        myFruits.append(allFruit.randomElement()!)
    }
}
A view containing buttn reading "NewFruit" and a ForEach statement that houses a VStack with a text view populated by a string array, "🍌🍌", "🍑🍑", "🍎🍎", called myFruits that the enveloping ForEach statement loops through as identifiable objects; the button calls a function that appends a new fruit to the string array, printing it to the VStack.

  • Note: This initializer works well for externally loaded data. It enables your app's frontend to automatically reflect data as it arrives.

Availability

iOS 13.0+

macOS 10.15+

tvOS 13.0+

watchOS 6.0+

Topics


Instance Property

content A function you can use to create content on demand using the underlying data

data The collection of underlying identified data that SwiftUI uses to create views dynamically.


Initializer

init(_:content:) Creates a view from data that conforms to Identifiable.

init(_:content:) Computes views over a given constant range.

init(_:id:content:) Creates an instance that uniquely identifies and computes views.


Type Alias

Body The type of view representing the body of this view.