Sponsor Link
環境&対象
- macOS14.6
- Xcode 16.1 Beta
- iOS 17.5
- Swift 5.9
Table
SwiftUI では、Table という表組みを表示するための View(Container) が用意されています。
参考
TableApple Developer Documentation
UIKit では、UITableView, AppKit では、NSTableView に相当するものです。
Lazy
SwiftUI に限らず、なんらかの要素をリストやテーブルに表示するときの懸念点としてその表示要素の計算量があります。
例えば、「100万要素あるデータのリストを表示する時に、100万要素分の表示要素を作り、そのうちの画面に表示される分だけを表示する」ということをしたくないということです。
現実的にも 100万要素の表示を同時に行える環境はほとんどないでしょうから、最初から表示されるであろう要素の部分だけ 表示要素を準備すれば処理を軽減できます。
同様のことは、DB の処理でも行われます。100万要素のデータをメモリ上に読み込むのは避けたいはずで、処理するために要求されたデータのみをメモリ上に読み込み 不要なデータは読み込まないということをします。(DB として考えると通常のことです・・・)
このような必要部分のみを読み込み処理することは lazy 処理 と呼ばれ、View名に付与されていたりします。
(英語の lazy は 怠惰 の意味を持つことから、必要以上に処理しないことを指しています)
SwiftUI では、VStack / HStack と同様のレイアウトを lazy 処理 でおこなう View(Container) として、LazyVStack / LazyHStack が用意されています。
参考
LazyVStackApple Developer Documentation
参考
LazyHStackApple Developer Documentation
VStack/HStack では、このように、lazy かどうかで 別の View(Container) が用意されています。
そのほかに、Grid も lazy バージョンが用意されています。
Table は、Lazy なのか?
Table も大量のデータを扱うケースが出てきそうですので、VStack/HStack と同様な lazy 版が欲しくなります。
ですが、Table については、LazyTable という View(Container) は用意されていません。
Table が常に Lazy に処理してくれるのもありがたい振る舞いですが、ドキュメントを確認しても、lazy に振る舞うかどうかは言及されていません。
実際にサンプルコードを書いて、どのような振る舞いになっているのか確認してみました。
テストコード
以下では 必要な分だけ Row を生成しているかどうかで lazy であるかどうかの判定としています。
使用したコードは以下です。
//
// ContentView.swift
//
// Created by : Tomoaki Yagishita on 2024/09/02
// © 2024 SmallDeskSoftware
//
import SwiftUI
extension Int: Identifiable {
public var id: Int { self }
}
class Model: ObservableObject {
var numbers: [Int] = (1..1_000_000).map({ $0 })
}
struct ContentView: View {
@StateObject private var model = Model()
var body: some View {
VStack {
Text("Hello, world!")
Table(of: Int.self, columns: {
TableColumn("Index") { item in
Text(String(item))
}
}, rows: {
ForEach(model.numbers, id: \.self) { value in
let _ = { print("row for ", value) }()
TableRow(value)
}
})
}
.padding()
}
}
#Preview {
ContentView()
}
1 Column しか表示していないので、List でも良いケースではありますが、あくまで目的は Table が lazy かどうかを調べることです。
iOS で確認
実行した結果は以下です。左が、スクリーンショット 右がコンソール出力です
(なお、iOS18.1beta で確認しても同じ動作でした)
row for 1
row for 2
row for 3
row for 4
row for 5
row for 6
row for 7
row for 8
row for 9
row for 10
row for 11
row for 12
row for 13
row for 14
row for 15
row for 16
画面に表示される分の Row のみが表示処理されています。
lazy に処理されていると言って良いと思います。
macOS で確認
実行した結果は以下です。左が、スクリーンショット 右がコンソール出力です
(なお、環境構築の関係で、macOS14.6 でのみ確認し、macOS15 beta では確認していません)
row for 1
row for 2
~~ 中略 ~~
row for 197
row for 198
row for 199
row for 200
row for 999899
row for 999900
~~ 中略 ~~
row for 999902
row for 999995
row for 999996
row for 999997
row for 999998
row for 999999
スクリーンショットを見てもわかりますが、表示量は、(ウィンドウサイズの関係で) iOS よりも少し少ないです。
ですが、最初の 200行分と最後の100行分が処理されています。
理由は不明です。
この状態を lazy と呼んで良いかは不明です・・・
100万行分処理されるよりは良いですが、14行のために、300行分処理されるのは、行で行われる処理によっては 問題になるかもしれません。
考察など
SwiftUI では、iOS 版ではきちんと動作するが、macOS 版では不思議な動作をするというケースが たまに見られますが、Table もその1つに思えます。
以前は そもそも 全 Row を評価するという動作だったので、あきらめて、NSTableView に切り替えたことがありましたが、その時点からは改善されているように思えます。
ただ、iOS と違って、macOS では、複数の表が同時に表示されていることが考えられるので、パフォーマンスの問題が発生した時には、疑うべき点の1つな気がします。
まとめ
Table は、Lazy なのかを確認しました。
- iOS では、Table は lazy な動作を行う。
- macOS では、Table は全 Row を処理はしないが、必要最小限ではない
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
SwiftUI おすすめ本
SwiftUI を理解するには、以下の本がおすすめです。
SwiftUI ViewMatery
SwiftUI で開発していくときに、ViewやLayoutのための適切なmodifierを探すのが大変です。
英語での説明になってしまいますが、以下の”SwiftUI Views Mastery Bundle”という本がビジュアル的に確認して探せるので、便利です。
英語ではありますが、1ページに コードと画面が並んでいるので、非常にわかりやすいです。
View に適用できる modifier もわかりやすく説明されているので、ビューの理解だけではなく、どのような装飾ができるかも簡単にわかります。
超便利です
販売元のページは、こちらです。
SwiftUI 徹底入門
# SwiftUI は、毎年大きく改善されていますので、少し古くなってしまいましたが、いまでも 定番本です。
Swift学習におすすめの本
詳解Swift
Swift の学習には、詳解 Swift という書籍が、おすすめです。
著者は、Swift の初期から書籍を出していますし、Swift の前に主力言語だった Objective-C という言語についても同様の書籍を出しています。
最新版を購入するのがおすすめです。
現時点では、上記の Swift 5 に対応した第5版が最新版です。
Sponsor Link