I have the following view.
import SwiftUI
struct AppView: View {
@EnvironmentObject var appStore: AppStore
var body: some View {
ZStack {
Color.blue
if .game == self.appStore.appMode {
GameView()
} else if .options == self.appStore.appMode {
OptionsView()
} else {
MenuView()
}
}
}
}
I want to animate the switching between the child views. I have seen some examples using a state for one switch, but I'm relying on more than one. I also tried to apply the animation inside a child view with onAppear
and onDisappear
. That works, but when the view is not shown, the onDisappear
does not get executed anymore. Besides, I would like to make it work for all the views.
Is there any way to do this without using multiple states? I would love to use only .transition
and .animation
.
Right now my best solution is this.
import SwiftUI
struct AppView: View {
@EnvironmentObject var appStore: AppStore
var body: some View {
ZStack {
Color.blue
if .game == self.appStore.appMode {
GameView()
.transition(.scale)
.zIndex(1) // to keep the views on top, however this needs to be changed when the active child view changes.
} else if .options == self.appStore.appMode {
OptionsView()
.transition(.scale)
.zIndex(2)
} else {
MenuView()
.transition(.scale)
.zIndex(3)
}
}
}
}
And on every view changer.
.onTapGesture {
withAnimation(.easeInOut(duration: 2)) {
self.appStore.appMode = .game
}
}
With the following approach you can modify your appMode
as you wish (onAppear, onTapGesture, etc.). Animation duration is, of course, can be set any you wish (as well as kind of transition, actually, however some transitions behaves bad in Preview, and should be tested on Simulator or real device).
Demo: (first blink is just Preview launch, then two transitions for onAppear, then for onTap)
Tested with Xcode 11.4 / iOS 13.4 - works and in Preview and in Simulator.
Code: Important parts marked with comments inline.
var body: some View {
ZStack {
// !! to keep background deepest, `cause it affects removing transition
Color.blue.zIndex(-1)
// !! keep any view in explicit own `if` (don't use `else`)
if .game == self.appStore.appMode {
GameView()
.transition(AnyTransition.scale.animation(.easeInOut(duration: 1)))
}
if .options == self.appStore.appMode {
OptionsView()
.transition(AnyTransition.scale.animation(.easeInOut(duration: 1)))
}
if .menu == self.appStore.appMode {
MenuView()
.transition(AnyTransition.scale.animation(.easeInOut(duration: 1)))
}
}
}
Update: for .slide
(and probably other moving transitions) the change of state should be wrapped into explicit withAnimation
, like below
withAnimation {
self.appStore.appMode = new_mode_here
}
Note: these is one of transitions that is not supported by Preview - test in Simulator.
Example for transition like
...
if .menu == self.appStore.appMode {
Text("MenuView").frame(width: 300, height: 100).background(Color.red)
.transition(AnyTransition.move(edge: .bottom).combined(with: .opacity).animation(.easeInOut(duration: 1)))
}
}
.onTapGesture {
withAnimation {
let next = self.appStore.appMode.rawValue + 1
self.appStore.appMode = next > 2 ? .game : AppStore.AppMode(rawValue: next)!
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.