2
3

LazyVStackの孫ViewのStateが画面外でリセットされる

Posted at

SwiftUIのLazyVStackの孫ViewのStateが画面外でリセットされる

とても長いタイトルですが、タイトルままです
他の方が報告されている、こちらのOpen Radarと同じ問題です

iOS 17.2.1、Xcode 15で確認してます

起きている現象

例えば下記のようにLazyVStackの子ViewとしてChildView、ChildViewの子ViewとしてSubViewを配置します
(つまりLazyVStackから見たら、SubViewは孫View)
この時、SubViewの持つ@State及び@StateObjectが、そのSubViewが画面外に行った際にリセットされてしまいます
ChildViewの方は値が保持されているので、おそらく不具合かなと考えています

struct ContentView: View {
    @State var items: [String] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".map { String($0) }
    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(items, id: \.self) { ChildView(item: $0) }
            }
        }
    }
}

// 子View
struct ChildView: View {
    let item: String
    @State var flag = false
    var body: some View {
        HStack {
            Toggle("\(item): ChildView", isOn: $flag)
                .fixedSize()
            SubView()
        }
        .padding()
        .border(Color.blue)
    }
}

// 孫View
struct SubView: View {
    @State var flag = false
    var body: some View {
        Toggle("SubView", isOn: $flag)
            .fixedSize()
            .padding()
            .border(Color.blue)
    }
}

対策

ChildViewで値を持ち、SubViewにBindingで渡すのがシンプルな解決策だと思います

 struct ChildView: View {
     let item: String
     @State var flag = false
+    @State var flagForSubView = false
     var body: some View {
         HStack {
             Toggle("\(item): ChildView", isOn: $flag)
                 .fixedSize()
+            SubView(flag: $flagForSubView)
-            SubView()
         }
         .padding()
         .border(Color.blue)
     } 
 }

 struct SubView: View {
+    @Binding var flag: Bool
-    @State var flag = false
     var body: some View {
         Toggle("SubView", isOn: $flag)
             .fixedSize()
             .padding()
             .border(Color.blue)
     }
 }

しかし、SubViewを共通化しライブラリとして切り出している場合、親ViewにBindingを要求するのは若干、ライブラリとしての使い勝手が悪くなりがちです
そこで、ワークアラウンド(と言っていいか怪しいレベル)ですが、@State、@StateObjectを付けなければ値は保持されるので、attributeアリとナシのプロパティそれぞれで値を保持し、View内で適切に復元するロジックを組む事で、一応状態を維持できます
画像キャッシュライブラリのKingfisherのissueの議論がとても参考になりました

終わりに

遭遇したSwiftUIのLazyVStackの不具合についての話でした
もし、より良い解決手段をご存知でしたら教えて頂けるととても嬉しいです!

2
3
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
2
3