Listは、SwiftUIでデータを垂直スクロール可能なリストとして表示するためのコンテナViewで、UIKitのUITableViewに相当します。Apple Developer Documentation, 2024によると、Listは静的および動的セクション、スワイプアクション、行の並べ替え、プル・トゥ・リフレッシュをサポートしています。UITableViewとは異なり、ListはSwiftUIとForEachに基づく宣言的APIを使用し、セルの再利用と多数の行に対するパフォーマンスを自動的に管理します。
重要ポイント
Listは、アイテムのシーケンスを垂直スクロール可能なリストに表示するViewです。iOS 13でSwiftUIとともに導入され、UIKitのUITableViewを置き換えるデータリスト表示の主要な方法です。Listはセルの再利用、スクロール、パフォーマンスを自動的に管理します。
Listは遅延ロードを使用します:セルはスクロールに応じて作成され、一度に全てが作成されるわけではありません。これにより、ScrollView内のVStackとForEachとは異なり、レンダリング時にすべてのセルが作成されることはありません。Listはスワイプアクション、プル・トゥ・リフレッシュ、編集(削除/移動)、行選択の組み込みサポートも提供します。
Apple WWDC 2021(Session 10072)によると、iOS 15+のListは、コレクションレベルでの新しい差分メカニズムにより、大幅なパフォーマンス向上を実現しました。これにより、特に数百行のリストにおいて、データ更新時のListの効率が向上しました。
開発者は、Viewのセットを表示する際にListとScrollView + VStackの間で選択することがよくあります。主な違い:Listはセルの再利用(UITableViewと同様)を使用しますが、ScrollView + VStackはすべてのViewを即座に作成します。固定サイズのリスト(最大20項目)の場合、違いはわずかです。50行以上の動的リストの場合、パフォーマンスの面でListが推奨されます。
静的リストは、List本体で直接指定された固定数の行を持つリストです。メニュー、設定、既知のアイテムセットを持つフォームに使用されます。各行はループやForEachなしで明示的に宣言されます。
// 静的リスト(メニューと設定用)
List {
Text("プロフィール")
Text("設定")
Text("について")
}
// 動的リスト(データ用)
struct UserList: View {
let users: [User]
var body: some View {
List(users) { user in
HStack {
Text(user.name)
Text(user.role)
.foregroundColor(.secondary)
}
}
}
}
動的リストは、List(data:rowContent:)イニシャライザまたはList本体内部のForEachを使用します。最初のアプローチは、各行が1つのデータ項目に対応する場合に便利です。2番目のアプローチは、データ間にセクションや追加の項目がある場合に役立ちます。
識別(Identifiable):動的リストの場合、データ項目はIdentifiableプロトコルに準拠するか、data:idタプルで一意の識別子へのKeyPathを指定する必要があります。SwiftUIは識別子を使用して変更(追加、削除、行の移動)を追跡します。
Sectionは、List内で行をヘッダーとオプションのフッターでグループ化するためのViewです。SectionはheaderとfooterをViewBuilderとして受け入れ、セクションヘッダーにテキストだけでなくカスタムViewを使用することもできます。
struct SettingsView: View {
var body: some View {
List {
Section(header: Text("アカウント")) {
Text("名前")
Text("メール")
}
Section(header: Text("通知")) {
Toggle("Push", isOn: $pushEnabled)
Toggle("Email", isOn: $emailEnabled)
}
}
.listStyle(.insetGrouped)
}
}
// ForEachを使用した動的セクション
List {
ForEach(groupedData.keys.sorted(), id: \.self) { key in
Section(header: Text(key)) {
ForEach(groupedData[key]!) { item in
Text(item.title)
}
}
}
}
Listスタイル:SwiftUIは.listStyle()修飾子を介して複数の組み込みスタイルを提供します。.insetGrouped — iOS Settingsの標準、.plain — ミニマリスト、.inset — インデント付き、.sidebar — iPadのSidebar用。
SwiftUI Cookbook(2024)によると、動的セクションと内部にForEachを持つSectionは、複雑な構造を持つアプリケーションでデータをグループ化するための標準的なパターンです。重要なルール:SectionをSectionの中にネストしないでください。また、Sectionと一緒にList(data:)イニシャライザを使用しないでください — List本体の内部でForEachを使用してください。
.swipeActions(edge:allowsFullSwipe:content:) — iOS 15+向けの修飾子で、Listの行にスワイプアクションを追加します。左(デフォルト)または右にスワイプしたときに、異なる色と役割(destructive、cancel)を持つボタンを表示できます。
struct TaskList: View {
@Binding var tasks: [Task]
var body: some View {
List {
ForEach($tasks) { $task in
Text(task.title)
.swipeActions(edge: .trailing) {
Button("削除", role: .destructive) {
tasks.removeAll { $0.id == task.id }
}
}
.swipeActions(edge: .leading) {
Button(task.isDone ? "元に戻す" : "完了") {
task.isDone.toggle()
}
.tint(.green)
}
}
}
.refreshable {
// 非同期データ読み込み
await loadTasks()
}
}
}
.refreshable — iOS 15+向けの修飾子で、プル・トゥ・リフレッシュを追加します。ユーザーがリストを下に引いたときに実行される非同期クロージャを受け入れます。SwiftUIは自動的にローディングインジケータを表示します。操作が完了すると、インジケータは非表示になります。
.onDeleteおよび.onMove — iOS 13+向けの修飾子で、行の削除と並べ替えのサポートを追加します。これらを使用するには、Bindingを使用してデータをForEachでラップするか、ListまたはForEachで.onDelete(perform:)を介してクロージャを渡します。
Listのパフォーマンスは、行数、各セルの複雑さ、データ更新の頻度に依存します。SwiftUIは遅延ロードとセルの再利用(UITableView.dequeueReusableCellと同様)を使用しますが、500行以上のリストでは追加の最適化が必要になる場合があります。
| 最適化 | 説明 | iOSバージョン |
|---|---|---|
| Identifiable | 各アイテムの一意のID | iOS 13+ |
| EquatableView | データが等しい場合の再描画を回避 | iOS 13+ |
| id(_:) | ID変更時にViewの再作成を強制 | iOS 13+ |
| .equatable() | Equatableによる厳密な比較 | iOS 15+ |
| Diffable data | 変更時の自動差分検出 | iOS 15+ |
問題1:頻繁な更新。リスト内のデータが頻繁に更新される場合(例:毎秒)、Listは状態が変わるたびに表示セルを再描画する可能性があります。解決策:データに構造体(値型)を使用します — SwiftUIは値を比較し、変更された行のみを再描画します。
問題2:重いセル。各行に複雑なView階層、画像、アニメーションが含まれている場合、スクロールが遅くなる可能性があります。解決策:セルを個別のViewに抽出し、EquatableViewを使用して不要な再描画を防ぎます。SwiftUI Lab(2024)によると、複雑な行をサブコンポーネントに分割すると、レンダリング時間が30~50%短縮されます。
問題3:大量の行。1000行以上の場合、Listは遅延ロードにより効率的に動作しますが、レイアウト計算により初期ロードが遅くなる可能性があります。解決策:Listの機能(スワイプ、セクション)が必要ない均一な行のリストにはLazyVStackを使用します。フル機能のリストには、Listが最良の選択肢です。
よくある質問
Listは、SwiftUIでスクロール可能なデータリストを表示するためのコンテナViewです。宣言的APIを持つUIKitのUITableViewに相当します。セクション、スワイプアクション、プル・トゥ・リフレッシュ、編集、.listStyle()によるカスタマイズをサポートしています。
Listは遅延ロードとセルの再利用を使用します — セルはスクロールに応じて作成されます。ScrollView + VStackはすべてのViewを一度に作成します。50行以上のリストではListが推奨されます。固定の小規模セット(最大20項目)の場合、違いはわずかです。
.refreshable修飾子(iOS 15+)を使用します。データ更新ロジックを含む非同期クロージャを渡します。SwiftUIは自動的にローディングインジケータを表示し、非同期操作の完了後に非表示にします。
ヘッダーとオプションのフッターを持つSection Viewを使用します。Section内にリスト行を配置します。動的セクションには、groupedDataとともにForEachを使用します。iOSのような外観にするには、.listStyle(.insetGrouped)でリストスタイルを設定します。
データに構造体(値型)を使用し、複雑なセルをEquatableViewを持つ個別のViewに抽出し、各行での頻繁な状態更新を避けます。1000行以上のリストでListの機能が必要ない場合は、LazyVStackを検討してください。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。