はじめに
昨年からSwiftUIを使いはじめて、約1年が経ちました。その間様々な問題が発生し、例えば値が変わっても自動で画面が更新されなかったり、予期せぬ動作や表示が崩れたりといったことがありました。それらの解決策や注意する点をまとめていきます。
画面更新
まずは画面更新にまつわる注意点をあげます。基本的にSwiftUIでは@Stateや@Bindingを変数につけることで、値が変わった時に画面に更新処理が入ります。
forEach
SwiftUIではループ処理としてForEachを使います。引数にはRange<Int>型を入れ、その間の分だけ繰り返すようになっています。
しかし、
@State range = 0..<5
.
.
.
range = 6..<10
ForEach(range) { index in
// 繰り返したい処理
}
のようなコードはrangeが変更されてもindexは0から5までにしかなりません。この場合は引数にidを入れる必要があります。
ForEach(range, id: \.self) { index in
// 繰り返したい処理
}
ForEachにはもう一つ気をつける点がありまして、iOSのバージョンによってはRangeが変数でなくても、更新されない時があります。その場合でも上記のコードのように引数にidを入れれば解決します。
今のところ、idをして不具合を見つけていないので、迷ったらつけてしまった方がいいような気がします。
他View間は更新されない
VStack {
ContentView1()
ContentView2()
}
このような時、端末上では一つのViewとして表示されています。しかし、ContentView2で値が更新されてもContentView1は更新されません。当たり前かもしれませんが、案外見落としてしまうところでもあります。
ちなみにNotificationCenterなどで更新したいViewのObservableObjectで更新処理を走らせれば大丈夫です。
@ObservedObject var viewModel: ContentView1Model
viewModel.objectWillChange.send()
その他
.sheetや.alertは複数使う時には注意が必要です。
VStack {
}
.sheet {
// 表示したいView
}
.sheet {
// 表示したいView
}
とやると片方しか表示されません。なので
Color.clear
.sheet
のように.sheetを複数回使う時は都度Color.clearなどのViewを用意しないと表示されません。(他の方法もありますが割愛します。)
不具合
ios13
ios13はそれより後と比べ挙動が異なることが多かったです。例えば、上記のその他で使ったようなColor.clearを使った場合、ios13ではフレームが幅高さ両方とも0にはなりません。なのでViewに白いスペースができてしまうのです。
こちらはframe(width: 0, height: 0)とすれば問題なくなります。
他にも.alertが機能していなかったり、ios14以降とはViewのサイズが異なっていたり、文字サイズが崩れたりと結構ありました。ios13にも対応する場合は何度も確認した方がいいですね。(足切りしてしまった方が安全ですが。)
EnvironmentObject
2個前のViewに戻りたい時にフラグを保存するために使っていたのですが、画面が行ったり、きたりとおかしな挙動をしました。この時はシングルトンを使うと正常な動作になりました。ちなみに今のところ、シングルトンで問題は起きていません。
参考: https://stackoverflow.com/questions/61363361/environmentobject-vs-singleton-in-swiftui
おわりに
とにかくios13にはかなり苦労させられました。(まあ13での確認を怠ったことが悪いのですが)皆様もお気をつけください。気づいた時にはViewはぐちゃぐちゃ、クラッシュまみれなんていうことになりかねません。