ForEachを使った一覧表示

2021年03月09日

複数の要素を一覧表示したい場合、SwiftUIでは ForEach を使用します

struct ContentView: View {
    var texts = ["a", "b", "c", "d", "e"]
    var body: some View {
        VStack {
            ForEach(texts, id: \.self, content: { t in
                Text(t)
            })
        }
    }
}



クロージャの最後の関数を外に出す機能を使用して書かれる事が多いため、以下のように記述される事が多いです

struct ContentView: View {
    var texts = ["a", "b", "c", "d", "e"]
    var body: some View {
        VStack {
            ForEach(texts, id: \.self) { t in
                Text(t)
            }
        }
    }
}
forEachを使ってTextを順番に表示する



↑ では VStack でラップしているため縦並びになっていますが、 HStack を使えば横並びになります。

struct ContentView: View {
    var texts = ["a", "b", "c", "d", "e"]
    var body: some View {
        HStack {
            ForEach(texts, id: \.self) { t in
                Text(t)
            }
        }
    }
}
forEachはHStackを使うことで横並びにTextを表示することも出来る

idについて

idにはForEachでループさせる値を一意に識別出来るプロパティを*** KeyPath を用いて指定***します。


そしてForEachでループさせる値に 追加、更新、削除 を行った場合に id を用いて再描画がされます。

例えば以下のような User というオブジェクトを作った場合 id には以下のように指定が出来ます。

struct User {
    var name: String
    var age: Int
}

struct ContentView: View {
    var users = [User(name: "Hanako", age: 20), User(name: "Taro", age: 30)]
    var body: some View {
        VStack {
            ForEach(users, id: \User.name) { user in
                Text("\(user.name) is \(user.age)")
            }
        }
    }
}
UserをKeyPathを用いて表示する

\User.name の部分は省略系を使って \.name と記載する事がほとんどです。

ForEach(users, id: \.name) { user in
    Text("\(user.name) is \(user.age)")
}




また上記の例では \.name を使用しましたが、もちろん \.age を使用する事も出来ます。

ForEach(users, id: \.age) { user in
    Text("\(user.name) is \(user.age)")
}

ただし実際のプロジェクトの場合、nameageも一意であるかを判定しづらいケースがほとんどと思われるため、
どちらもいい例ではありません。



Identifiable に準拠させるのが望ましいでしょう。

Identifiableを使う

Identifiable に準拠すると id というプロパティを持つ事が必須になります。

public protocol Identifiable {

    /// A type representing the stable identity of the entity associated with `self`.
    associatedtype ID : Hashable

    /// The stable identity of the entity associated with `self`.
    var id: Self.ID { get }
}




User に対して Identifiable を準拠させると以下のように書けます

struct User: Identifiable {
    var id = UUID()
    var name: String
    var age: Int
}

struct ContentView: View {
    var users = [User(name: "Hanako", age: 20), User(name: "Taro", age: 30)]
    var body: some View {
        VStack {
            ForEach(users, id: \.id) { user in
                Text("\(user.name) is \(user.age)")
            }
        }
    }
}



更にForEachでループさせる値が Identifiable に準拠している場合、
id: を省略して書けるため以下のように書けます

struct User: Identifiable {
    var id = UUID()
    var name: String
    var age: Int
}

struct ContentView: View {
    var users = [User(name: "Hanako", age: 20), User(name: "Taro", age: 30)]
    var body: some View {
        VStack {
            ForEach(users) { user in
                Text("\(user.name) is \(user.age)")
            }
        }
    }
}

おすすめ