何らかの状態に合わせて表示する画面を丸ごと切り替えを行いたい場合にアニメーションをするには?というテーマです。
例えば、ログイン状態とログアウト状態で画面を切り替える時など。
ZStackの中で、if文で頑張っても良いのですが、switch-caseが使えないなどの制限された状況では条件分岐に限界が来ると思ったので、別の方法を考えていました。
最初に思いついたのが次のようなViewです。
struct FragmentView: View {
let content: AnyView
init(factory: () -> AnyView) {
self.content = factory()
}
var body: some View {
content
}
}
次のように使います。
FunctionBuilerから逃げているので、表示したいViewをreturnする必要があります。
var body: some View {
FragmentView {
switch ... {
case ...: return AnyView(...)
case ...: return AnyView(...)
}
}
これで表示する画面を切り替えることには成功していたのですが、アニメーションがうまいこといきませんでした。
例えば次のようなコードです
var body: some View {
FragmentView {
switch ... {
case ...: return AnyView(...)
case ...: return AnyView(...)
}
.transition(.opacity)
.animation(.easeInOut)
}
推測ですが、SwiftUI側でうまく差分が出せないことによってtransitionが動作しなかったのかも?
そこで、ForEachを利用して同じような実装を行ってみます。
struct ContainerView: View {
var views: [AnyView] = []
init(make: (inout [AnyView]) -> Void) {
make(&views)
}
var body: some View {
ZStack {
ForEach(views.indices) { i in
views[i]
}
}
}
}
状態に合わせて表示したいViewをviewsにappendします。
struct RootView: View {
@EnvironmentObject var rootStore: RootStore
var body: some View {
ContainerView { views in
switch rootStore.state.loginState {
case .loggedOut(let state):
views.append(AnyView(LoggedOut(...)))
case .loggedIn(let state):
views.append(AnyView(LoggedIn(...)))
}
}
.transition(.opacity)
.animation(.easeInOut)
}
}
これで一応loggedOutからloggedInに切り替わる際にフェードで遷移するようになりました。
このアプローチによるメリットは、パターンの数に限界がないことです。
ZStack, VStackなどでは持てるViewの数は10個が最大値となっています。(TupleViewの解析が10個まで。)
他に良い方法あるかな?