LoginSignup
7
5

【SwiftUI】LazyVStackのonAppearについて調査した

Last updated at Posted at 2023-12-17

こんにちは。ymurao2です!
ScrollView + LazyVStackで縦に長い画面を実装しているとき、VStackLazyVStackの使い分けについて悩むことが少なからずあったので、調査しました

動作環境
Xcode15.0.1

以下のような画面で調査しました
CleanShot 2023-12-17 at 14.40.14.gif

調査コード

ScrollView {
+   // ここと
    LazyVStack {
+       // ここと
        LazyVStack {
            ForEach(0..<25, id: \.self) { i in
                Text(String(i))
                    .padding()
                    .onAppear {
                        print("upper:" + String(i))
                    }
            }
        }
        .background(Color.gray)
+       // ここと
        LazyVStack {
            ForEach(25..<50, id: \.self) { i in
                Text(String(i))
                    .padding()
                    .onAppear {
                        print("middle:" + String(i))
                    }
            }
        }
        .background(Color.red)
+       // ここをいじります
        LazyVStack {
            ForEach(50..<75, id: \.self) { i in
                Text(String(i))
                    .padding()
                    .onAppear {
                        print("lower:" + String(i))
                    }
            }
        }
        .background(Color.blue)
    }
}

1.LavyVStack + LazyVStack

ScrollView {
    LazyVStack {
        LazyVStack {
            // 省略
        }
        .background(Color.gray)
        LazyVStack {
            // 省略
        }
        .background(Color.red)
        LazyVStack {
            // 省略
        }
        .background(Color.blue)
    }
}

CleanShot 2023-12-17 at 14.50.41.gif

Viewが表示される前にonAppearは発火しているので、ログは実際に表示されるタイミングより早く表示されています
Stack Overflow参照

このパターンの場合、全てのViewが遅延読み込みされていました

2.LazyVStack + VStack

// 最初のコードとのdiff
ScrollView {
    LazyVStack {
+       VStack {
            // 省略
        }
        .background(Color.gray)
+       VStack {
            // 省略
        }
        .background(Color.red)
+       VStack {
            // 省略
        }
        .background(Color.blue)
    }
}

CleanShot 2023-12-17 at 15.04.15.gif

このパターンの場合、VStackの先頭がレンダリングされるときに、VStackの内のonAppear全て発火していました

3.VStack + LazyVStack

// 最初のコードとのdiff
ScrollView {
+   VStack {
        LazyVStack {
            // 省略
        }
        .background(Color.gray)
        LazyVStack {
            // 省略
        }
        .background(Color.red)
        LazyVStack {
            // 省略
        }
        .background(Color.blue)
    }
}

CleanShot 2023-12-17 at 15.10.03.gif

1.のLavyVStack + LazyVStackと同じ結果になりました

このパターンも良さそうに見えましたが、別の問題がありました

// 最初のコードとのdiff
ScrollView {
    VStack {
        LazyVStack {
            // 省略
        }
        .background(Color.gray)
        LazyVStack {
            // 省略
        }
        .background(Color.red)
        LazyVStack {
            // 省略
        }
        .background(Color.blue)
+       Text("表示されたぞ!!!")
+           .onAppear {
+               print("表示されたぞ!!!")
+           }
    }
}

CleanShot 2023-12-17 at 15.25.40.gif
一番下にTextを差し込んだところ、こちらは表示される前にonAppearが発火しました
これは期待通りの動作とは言えなさそうです

4.VStack + VStack

// 最初のコードとのdiff
ScrollView {
+   VStack {
+       VStack {
            // 省略
        }
        .background(Color.gray)
+       VStack {
            // 省略
        }
        .background(Color.red)
+       VStack {
            // 省略
        }
        .background(Color.blue)
    }
}

CleanShot 2023-12-17 at 15.12.44.gif
Gifだと分かりにくいですが、全てのViewonAppearが一度に発火していました

まとめ

実装方法 結果 評価
LavyVStack + LazyVStack 全てのViewが遅延読み込みされた
LazyVStack + VStack VStack内のViewonAppearが同時に発火 ×
LazyVStack + VStack VStack内の要素が全てVStackに入っていれば問題なさそう
VStack + VStack 全てのViewが遅延読み込みされない ×

調査の結果、LazyVStack + LazyVStackで実装するのが良さそうに見えました

この記事がお役に立てば、ぜひいいね👍お願いします!

参考文献

LazyVStackを使用すると描画処理が何度も走ってしまう
lazyvstack-row-onappear-is-called-early

7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5