#はじめに
昨年から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はぐちゃぐちゃ、クラッシュまみれなんていうことになりかねません。