GeometryReader下で@AppStorageでマークした変数を使用して描画を行なっていても、
変数の変化により再描画されない場合があります。
こういった場合はGeometryReaderを別のViewに分ける事で解決します。
@Stateや@SceneStorageで作って置いて、後から@AppStorageに変えると気づき難いので注意しましょう。
再現するコード
今回はXcode14.2、iOS16.2で実行しています。
struct ContentView: View {
@State private var stateFlag = true
@AppStorage("AppStorage") private var appStorageFlag = true
var body: some View {
GeometryReader { geometry in
VStack {
HStack {
Button("StateFlag"){
stateFlag.toggle()
}
Text(stateFlag ? "True" : "False")
}.padding()
HStack {
Button("AppStorageFlag (Issue)"){
appStorageFlag.toggle()
}
Text(appStorageFlag ? "True" : "False")
}.padding()
ChildView().padding()
}
}.padding()
}
}
struct ChildView:View{
@AppStorage("AppStorage") private var appStorageFlag = true
var body: some View{
VStack {
HStack {
Button("AppStorageFlag in Child"){
appStorageFlag.toggle()
}
Text(appStorageFlag ? "True" : "False")
}
}
}
}
解説
この問題は@AppStorageとGeometryReaderが同一Viewに存在していると発生します。
この場合に正常に変更を検知せず再描画されません。
1度目の変更のみ再描画されて2度目以降は再描画されなくなっています。
@Stateの方は問題なく再描画されます。※SceneStorageも問題なく動きます。
また@AppStorageも値自体は変わっているのでこの時に表示が変わります。
解決法としてはChildViewのように@AppStorageをGeometryReaderと別のViewにしてしまえば問題ありません。
なお、この時ChildViewで@AppStorageを変更しても、親のContentView側では変更を検知できていないので再描画されません。
ChildViewでGeometryReaderの値を使用したい場合はGeometryProxyを渡してください。
GeometryReader { geometry in
ChildView(geometry:geometry)
}
struct ChildView:View{
let geometry:GeometryProxy
@AppStorage("AppStorage") private var appStorageFlag = true
}
コメント