【SwiftUI】List の使い方【総まとめ】

過去記事でも List については触れていましたが、使い方に色々とパターンがあるので、筆者自身未だに「この場合はどうするんだっけ」となることがあるので改めてまとめてみようと思います。

List の使い方【総まとめ】

7つのパターンについて解説します。

(1) 固定表示

シンプルに Text を要素として並べたものになります。

予め表示するものが固定で決まっている場合にこのような使い方をすることがありますが、Form を使った表示方法を検討しても良いと思います。

Form についてはこちらの記事をご参考ください。

struct ContentView: View {
    var body: some View {
        List {
            Text("リスト1")
            Text("リスト2")
            Text("リスト3")
            Text("リスト4")
            Text("リスト5")
        }
    }
}

(2) 配列を使った表示

この例では固定配列としていますが、WebAPI等から取得したデータを割り当てる場合も同様です。

引数の id: は要素ごとの識別子の指定です。List は Identifiable プロトコルに準拠したデータ((4)で解説します)であれば id: の指定は不要ですStringIntIdentifiable に準拠していませんので、ここでは \.self でこのクラス名自体を識別子として代用しています。

struct ContentView: View {

    let array: [String] = [
        "リスト1",
        "リスト2",
        "リスト3",
        "リスト4",
        "リスト5",
    ]

    var body: some View {
        List(array, id: \.self) { text in
            Text(text)
        }
    }
}

(3) 配列を使った表示(index指定バージョン)

配列の引数を「配列名.indices」とすると、クロージャ引数として配列の index を取得することができ、Text(array[index]) という形式と取ることができます。一見回りくどいやり方ですが、後述する(6) のパターンで生きてきます。

struct ContentView: View {

    let array: [String] = [
        "リスト1",
        "リスト2",
        "リスト3",
        "リスト4",
        "リスト5",
    ]

    var body: some View {
        List(array.indices, id:\.self) { index in
            Text(array[index])
        }
    }
}

(4) ForEach との組み合わせ

List の中身に ForEach を含ませることができます。下記例のように、固定要素と織り交ぜたい場合などに便利です。

struct ContentView: View {

    let array: [String] = [
        "リスト1",
        "リスト2",
        "リスト3",
        "リスト4",
        "リスト5",
    ]

    var body: some View {
        List {
            Text("先頭固定")
            ForEach(array, id:\.self) { text in
                Text(text)
            }
            Text("末尾固定")
        }
    }
}

(5) Identifiable に準拠したデータの表示

Identifiable プロトコルを継承し、id 要素を持った User 構造体を使った例です。予め id を保持したデータなので List(users) というシンプルな形式をとることができます。

struct ContentView: View {

    struct User: Identifiable {
        let id = UUID()
        let name: String
    }

    let users: [User] = [
        User(name: "User0001"),
        User(name: "User0002"),
        User(name: "User0003"),
        User(name: "User0004"),
        User(name: "User0005"),
    ]

    var body: some View {
        List(users) { user in
            Text(user.name)
        }
    }
}

(6) セル要素にデータを Binding させたい場合

要素のセルを別の View として切り出した場合に、(4) で説明した index 指定の形式を取ることでデータバインディングさせることができます。

下記の例ではセルをタップするとデータが書き変わり表示も更新されます。

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

struct ContentView: View {

    @State var users: [User] = [
        User(name: "User0001"),
        User(name: "User0002"),
        User(name: "User0003"),
        User(name: "User0004"),
        User(name: "User0005"),
    ]

    var body: some View {
        List(users.indices) { index in
            CellView(user: $users[index])
        }
    }
}

struct CellView: View {

    @Binding var user: User

    var body: some View {
        VStack {
            Spacer()
            Text(user.name)
            Button("タップして更新") {
                user.name = "User0010"
            }
            Spacer()
        }
    }
}

(7) enum 要素を展開する

配列のように、enum の要素を全て List に展開することもできます。enumCaseIterable プロトコルに準拠していると、allCases プロパティで配列化して受け取ることができ、クロージャ引数として各要素を取得できます。

struct ContentView: View {

    enum Family: String, CaseIterable {
        case mother = "母"
        case father = "父"
        case grandMother = "祖母"
        case grandFather = "祖父"
        case sister = "姉"
        case brother = "兄"
    }

    var body: some View {
        List(Family.allCases, id: \.self) { family in
            Text(family.rawValue)
        }
    }
}

以上、List についてまとめてみました。状況に応じた使い分けの参考になれば幸いです。

【関連記事】