【SwiftUI】Listの使い方を徹底解説~タップ判定やスワイプアクション、スタイル変更、背景色まで~

この記事では、SwiftUIでListを作る方法について徹底解説していきたいと思います。ListはUIkitで言うUITableViewのことですね。

Listの実装方法

簡単なリスト

struct ContentView: View {
    var body: some View {
        List {
            Text("りんご")
            Text("みかん")
            Text("スイカ")
        }
    }
}

このように、Listの中にTextを並べるだけで、実装できてしまいます。Storyboardを使っていた僕からすると衝撃です。。Textはいくらでも増やせます。

画像とテキスト

struct ContentView: View {
    var body: some View {
        List {
            HStack {
                Image(systemName: "car")
                Text("車")
            }
            HStack {
                Image(systemName: "bus")
                Text("バス")
            }
            HStack {
                Image(systemName: "tram")
                Text("電車")
            }
        }
    }
}

このように、HStackと組み合わせると、画像と文字を表示させることができます。

セクション(グループ)

グループ化されたリストのことですね。

struct ContentView: View {
    var body: some View {
        List {
            Section {
                Text("リンゴ")
                Text("みかん")
                Text("スイカ")
            }
            Section(header: Text("野菜")) {
                Text("トマト")
                Text("ブロッコリー")
                Text("にんじん")
            }
        }
    }
}

このように、Listの中をさらにSectionで囲うとグループにまとめられます。セクションにはheader: で、タイトルをつけることもできます。

ちなみに、headerは、以下のような書き方もできます。

Section {
    Text("リンゴ")
    Text("みかん")
    Text("スイカ")
} header: {
    Text("フルーツ")
}

配列をリストにする

配列をリストに入れるにはForEachを使います。

struct ContentView: View {
    let fruits = ["りんご", "みかん", "スイカ", "レモン"]
    
    var body: some View {
        List {
            ForEach(0 ..< fruits.count) { index in
                Text(fruits[index])
            }
        }
    }
}

ちなみに、イレギュラーですが、ForEachを使わなくてもリストを作ることができます。

struct ContentView: View {
    let fruits = ["りんご", "みかん", "スイカ", "レモン"]
    var body: some View {
        List(fruits, id: \.self) { fruit in
            Text(fruit)
        }
    }
}

Structの配列をリストにする

Structの配列を使う場合は、このように書きます。一つのセルに複数の情報を入れる場合は、こちらの実装の方が良さそうですね。

struct Fruits: Identifiable {
    var id: Int
    var name: String
    let price: Int
}

struct ContentView: View {
    let fruitsList = [
        Fruits(id: 0, name: "りんご", price: 200),
        Fruits(id: 1, name: "みかん", price: 150),
        Fruits(id: 2, name: "スイカ", price: 400),
        Fruits(id: 3, name: "レモン", price: 200)
    ]
    
    var body: some View {
        List(fruitsList) { fruitsList in
            HStack {
                Text(fruitsList.name)
                Text("\(fruitsList.price)円")
            }
        }
    }
}

セルのタップ判定

struct ContentView: View {
    var body: some View {
        List {
            Text("りんご")
                .onTapGesture {
                    print("りんごがタップされた。")
                }
            Text("みかん")
            Text("スイカ")
        }
    }
}

そのセルに、.onTapGestureをつけることでタップ判定を取得することができます。これで、「りんご」をタップすればprint文が実行されます。

ただ、これだと、タップ領域が文字のところ(ピンクで囲ったところ)しかありません。

そのため、以下のように記述すると、タップ領域がセル全体になります。これだとセルのどこをタップしてもprint文が実行されます。

struct ContentView: View {
    var body: some View {
        List {
            HStack {
                Text("りんご")
                Spacer()
            }
            .contentShape(Rectangle())
            .onTapGesture {
                print("りんごがタップされた。")
            }
            Text("みかん")
            Text("スイカ")
        }
    }
}

タップ時に画面遷移

セルをタップしたときに、画面遷移させるという場面が多いと思いますが、以下のコードで簡単に実装できます。

struct ContentView: View {
    let fruits = ["りんご", "みかん", "スイカ", "レモン"]
    
    var body: some View {
        NavigationView {
            List {
                ForEach(0 ..< fruits.count) { index in
                    NavigationLink(destination: Text(fruits[index])) {
                        Text(fruits[index])
                    }
                }
            }.navigationBarTitle("フルーツ")
        }
    }
}

NavigationViewで全体を囲い、セルの内容をNavigationLinkで囲うと、これだけで画面遷移ができちゃいます。あと、セルの左側にシェブロンマーク(>)が付きます。

ちなみに、NavigationLinkを使わずに、このシェブロンマークを表示させるには、以下のように書きます。普通に無理矢理というか、、、、画像をつけてるだけです。

struct ContentView: View {
    let fruits = ["りんご", "みかん", "スイカ", "レモン"]
    
    var body: some View {
        List {
            ForEach(0 ..< fruits.count) { index in
                HStack {
                    Text(fruits[index])
                    Image(systemName: "chevron.right")
                        .font(Font.system(size: 14, weight: .semibold))
                        .foregroundColor(.secondary)
                        .opacity(0.5)
                }
            }
        }
    }
}

スワイプアクション

スワイプのアクションは以下のように記述します。

struct ContentView: View {
    let fruits = ["りんご", "みかん", "スイカ", "レモン"]
    
    var body: some View {
        NavigationView {
            List {
                ForEach(0 ..< fruits.count) { index in
                    Text(fruits[index])
                        .swipeActions(edge: .trailing) {
                            Button(role: .destructive) {
                                // 処理
                            } label: {
                                Image(systemName: "trash.fill")
                            }
                        }
                        .swipeActions(edge: .leading) {
                            Button {
                                // 処理
                            } label: {
                                Image(systemName: "flag.fill")
                            }.tint(.orange)
                        }
                }
            }
        }
    }
}

.swipeActionsでスワイプアクションをつけることできます。

edge: は位置を決めることができて、.trailingが右で.leadingが左です。

label: {の中に、画像やテキストを入れます。その後に、.tint(.orange)で色を変えることができます。

注意

こちらは、iOS15から使えるモディファイアとなっております。そのため、対象OSを14.0(デフォルト)にしているとエラーになります。対象OSを15.0にするか、if #available(iOS 15.0, *) {で囲わなければなりません。

リストの色を変更する

セルの色を変更する

struct ContentView: View {
    var body: some View {
        List {
            Text("りんご")
                .listRowBackground(Color.red)
            Text("みかん")
                .listRowBackground(Color.blue)
            Text("スイカ")
                .listRowBackground(Color.green)
        }
    }
}

背景色を変更する

struct ContentView: View {
    var body: some View {
        List {
            Text("りんご")
            Text("みかん")
            Text("スイカ")
        }
        .onAppear {
            UITableView.appearance().backgroundColor = UIColor.blue
        }
    }
}

SwiftUIでは、背景色を変更できないので、UIKitを使って変更させています。

リストの表示を変更する

リストの線を削除する

.onAppear {
    UITableView.appearance().separatorColor = .clear
}

リストのスタイルを変更する

リストにはいくつかのスタイルがあります。上記のリストは全てiOS14以降のデフォルトのスタイルになっています。

以下のコードのように、.listStyle(DefaultListStyle())というふうにスタイルを決められるのですが、何も書かない場合・DefaultListStyle()を指定した場合は、OSのバージョンによってスタイルが決められます。

struct ContentView: View {
    var body: some View {
        List {
            Text("りんご")
            Text("みかん")
            Text("スイカ")
        }
        .listStyle(DefaultListStyle())
    }
}

PlainListStyle()

これは、ちょっと前のリストのスタイルですね。iOS13までこのリストでした。

.listStyle(PlainListStyle())

GroupedListStyle()

こちらは、背景色(セル部分以外)がグレーになるスタイルですね。

.listStyle(GroupedListStyle())

InsetListStyle()

こちらは、左右に余白ができるスタイルになります。iOS14で追加されました。

.listStyle(InsetListStyle())

InsetGroupedListStyle()

こちらは、iOS14以降のデフォルトスタイルですね。

.listStyle(InsetGroupedListStyle())

SidebarListStyle()

こちらは、右側のボタンを押すとリストが表示非表示の切り替えができます。iOS14で追加されました。

.listStyle(SidebarListStyle())

最後にまとめると、以下のスタイルがあります。

スタイル 使用可能OS 説明
DefaultListStyle() OSにあったスタイルが適応される
PlainListStyle() iOS14以前のデフォルトスタイル。非常にシンプル
GroupedListStyle() セル以外グレー色になる。
InsetListStyle() iOS14〜 左右に余白ができる。背景色は白。
InsetGroupedListStyle() iOS14〜 iOS14以降のデフォルトのスタイル。
SidebarListStyle() iOS14〜 セクションごとに表示非表示ができるスタイル。