SwiftUIの `LazyVStack`、`LazyHStack`、`List`： 画面に表示されたときのみビューを読み込む

本記事は画面上に表示されたときのみデータを読み込む（遅延読み込みする）ようにSwiftUIの `LazyVStack` 、`LazyHStack` 、`List` を使用することについてお話しします。また、本記事では通常の `VStack` （非推奨です） と `Form` を使用した結果についてもご説明します。

# 遅延読み込みが必要な理由

例えば、最も人気の本を表示するアプリケーションを開発している場合、本の画像をサーバーから読み込む必要があります。

本のリストビューを表示する際、すべての本の画像が同時にダウンロードされるのを避けるべきかもしれません。代わりに、現在画面上に表示されている画像のみを読み込むのが望ましいでしょう。ユーザーがより多くの本を表示するためにスクロールすると、それらの画像がダウンロードされます。

# スターターコード

[完成したプロジェクトが含まれているスターターコードは、Githubからダウンロードできます。](https://github.com/mszmagic/SwiftUI_Lazy-Loading)

SwiftUIがビューを読み込むタイミングをより正確に確認するために、ネットワークリクエストコードにprintステートメントがあり、そこからリクエストIDを出力します。

# `VStack`を使用 (遅延読み込みなし)

```swift
ScrollView {
    VStack {
        ForEach(Networking.shared.getAllCatIDs(), id: \.self) { catID in
            ItemDisplay(catID: catID)
        }
    }
}
```

`VStack` の使用を選ぶと、すべてのビューが最初に読み込まれるのが分かります。このプログラムがインターネットからデータをフェッチする場合、ユーザーがプログラムを開いた時に多くのデータがダウンロードされることになります。

<details><summary>詳細に関してはこちらをクリックしてください</summary><div>
id: 99
id: 98
id: 97
id: 96
id: 95
id: 94
id: 93
id: 92
id: 91
id: 90
id: 89
id: 88
id: 87
id: 86
id: 85
id: 84
id: 83
id: 82
id: 81
id: 80
id: 79
id: 78
id: 77
id: 76
id: 75
id: 74
id: 73
id: 72
id: 71
id: 70
id: 69
id: 68
id: 67
id: 66
id: 65
id: 64
id: 63
id: 62
id: 61
id: 60
id: 59
id: 58
id: 57
id: 56
id: 55
id: 54
id: 53
id: 52
id: 51
id: 50
id: 49
id: 48
id: 47
id: 46
id: 45
id: 44
id: 43
id: 42
id: 41
id: 40
id: 39
id: 38
id: 37
id: 36
id: 35
id: 34
id: 33
id: 32
id: 31
id: 30
id: 29
id: 28
id: 27
id: 26
id: 25
id: 24
id: 23
id: 22
id: 21
id: 20
id: 19
id: 18
id: 17
id: 16
id: 15
id: 14
id: 13
id: 12
id: 11
id: 10
id: 9
id: 8
id: 7
id: 6
id: 5
id: 4
id: 3
id: 2
id: 1
id: 0
</div></details>

# （推奨）`LazyVStack`の使用（または`LazyHStack`）

```swift
ScrollView {
    LazyVStack {
        ForEach(Networking.shared.getAllCatIDs(), id: \.self) { catID in
            ItemDisplay(catID: catID)
        }
    }
}
```

`LazyVStack` は一気に全データを読み込むものではありません。そうではなく、最大インデックス31までのデータを読み込むことになります（データを読み込む具体的な順序はありません）。ことらのログメッセージからわかるように

<details><summary>詳細に関してはこちらをクリックしてください</summary><div>
id: 21
id: 9
id: 10
id: 22
id: 1
id: 8
id: 28
id: 31
id: 13
id: 7
id: 5
id: 19
id: 15
id: 11
id: 30
id: 12
id: 14
id: 24
id: 27
id: 6
id: 23
id: 16
id: 2
id: 18
id: 29
id: 20
id: 4
id: 25
id: 17
id: 26
id: 3
id: 0
</div></details>

水平スタイルのディスプレイの場合には、データの表示に `LazyHStack` を使用することもできます。

デモプログラムをダウンロードして実際の動きをご覧ください。`LazyVStack` を下部にスクロールするに連れて、より多くのデータが自動的にロードされます。

# （推奨）`List`の使用

```swift
List(Networking.shared.getAllCatIDs(), id: \.self) { catID in
    ItemDisplay(catID: catID)
}
```

`List` を使用すると、ビューが表示された際に最初の数個のエレメントのみが読み込まれます。ビューを下にスクロールすると、自動的にさらに多くのデータが読み込まれていきます。

ログメッセージで確認できるように、`LazyVStack` や `LazyHStack` と比較すると、`List`ではデータが順番に読み込まれていきます：

<details><summary>詳細に関してはこちらをクリックしてください</summary><div>
id: 0
id: 1
id: 2
id: 3
id: 4
id: 5
id: 6
id: 7
id: 8
id: 9
id: 10
id: 11
id: 12
id: 13
id: 14
id: 15
id: 16
id: 17
id: 18
id: 19
</div></details>

# `Form`を使用

```swift
Form {
    ForEach(Networking.shared.getAllCatIDs(), id: \.self) { catID in
        ItemDisplay(catID: catID)
    }
}
```

また、 `Form` 要素内に `ForEach` を含めることもできます。これはビューが表示されたとき、最初の数個のデータ要素のみが読み込まれ、ユーザーがビューをスクロールダウンするにつれて追加のデータが自動的に読み込まれます。しかし、いくつかの要素が2回読み込まれているようです：

<details><summary>詳細に関してはこちらをクリックしてください</summary><div>
id: 0
id: 1
id: 2
id: 3
id: 4
id: 5
id: 6
id: 7
id: 8
id: 9
id: 10
id: 11
id: 12
id: 13
id: 14
id: 15
id: 16
id: 17
id: 18
id: 19
id: 0
id: 1
id: 2
id: 3
id: 4
id: 5
id: 6
id: 7
id: 8
id: 9
id: 10
id: 11
id: 12
id: 13
id: 14
id: 15
id: 16
id: 17
id: 18
id: 19
id: 19
id: 18
id: 17
id: 16
</div></details>

# 結論

大量のデータを読み込んで画面に表示する場合には、プログラムにおいて `LazyVStack` 、`LazyHStack` 、`List`ビューの使用を心がけるようにしてください。これにより、必要な際にビューに関連付けられたデータが読み込まれるようになります（画面にデータを表示する場合）。

---

:relaxed: [Twitter @MszPro](https://twitter.com/MszPro)

:sunny: [私の公開されているQiita記事のリストをカテゴリー別にご覧いただけます。](http://mszpro.com/ioskiji/)
<img width="150" src="https://cdn.mszmagic.com/static-web-content/AppClipImage_small.png">