sheetで画面遷移するとき値を保持していない

ボタンのアクションで@State変数の値を書き換えフラグをトグルして.sheetで画面遷移するとき、その変数が書き変わらない時があった。
sheetを一回閉じて他のViewをアクションさせてからだと値は書き変わった。
@EnvironmentObject にして対応、以下 はそのことをGPTに聞いたときの回答。

✅ その現象の正体

.sheet は 表示開始時に引数をキャプチャして保持し続ける ため、

  • sheet を開く前に書き換えた @State が
  • sheet の初期化時点の古い値のまま使われてしまう

という SwiftUI 特有の仕様です。

つまり:

sheet を閉じるまで “初期値の v” を使い続けてしまう

✅ @EnvironmentObject にしたら直る理由

EnvironmentObject や ObservableObject なら:

  • sheet の View が オブジェクト参照そのもの を持つ
  • プロパティが変更されると Publisher が通知
  • sheet 内の値が 自動で更新される

つまり sheet が「値のコピー」ではなく「参照」を共有するようになります。

📌 他の解決方法(EnvironmentObject の他にもいくつかある)

① sheet の view を “id 付き” でリフレッシュする(裏技)

value が変わった時に sheet 内部の View が再生成され、
キャプチャ問題を回避できます。

② Binding を渡す

シンプルで効果的。

こうすれば sheet は 常に最新の $value を参照できます。
・EnvironmentObject ほど大げさじゃない
・NavigationStack の動作とも相性が良い

これは最も “SwiftUI らしい” 解決法。

③ sheet の presentation detent を一度閉じて強制再生成

推奨はしませんが、技術的には可能。

④ sheet(item:) を使わずに sheet(isPresented:) を使う

.sheet(item:) は特にキャプチャが強いので、
.sheet(isPresented:) の方が素直です。

✔️ まとめ(何が一番正しい?)

方法メリットデメリット
EnvironmentObject(採用済)状態共有が強い。大規模向けオブジェクトが大きくなりがち
Binding を渡す(推奨)最新状態を確実に反映。シンプル渡す変数が多いと煩雑
id(value) を使うコード量が少ないsheet が毎回再生成される
isPresented を使うキャプチャ問題が軽い引数を埋め込みたかった場合に工夫が必要

コメント

タイトルとURLをコピーしました