Let's say, I have a simple profile screen like that:
class Model: ObservableObject {
@Published var isSignedIn = false
init() {}
func login() {
//Some networking here
isSignedIn = true
}
func logout() {
//Some networking here
isSignedIn = false
}
}
struct ContentView: View {
@ObservedObject var model = Model()
var body: some View {
ZStack {
//ProfileView
VStack {
//Some Views with WithAnimation inside
// ...
Text("Hello, Dear User!")
Button(action: {
self.model.logout()
}) {
Text("Sign Out")
}
}
.opacity(model.isSignedIn ? 1 : 0)
//LoginView
VStack {
Text("Hello, Stranger")
Button(action: {
self.model.login()
}) {
Text("Sign In")
}
}
.opacity(model.isSignedIn ? 0 : 1)
}
}
}
And I want to apply animation to opacity changing.
The first approach is to use .animation
modifier. But it has certain drawbacks: it does not work properly if inner view has WithAnimation
- it overrides animation that was set with WithAnimation
.
My second approach to use .onReceive
:
class Model: ObservableObject {
@Published var isSignedIn = false
init() {}
func login() {
isSignedIn = true
}
func logout() {
isSignedIn = false
}
}
struct ContentView: View {
@ObservedObject var model = Model()
@State var isSignedIn = false
var body: some View {
ZStack {
//ProfileView
VStack {
Text("Hello, Dear User!")
Button(action: {
self.model.logout()
}) {
Text("Sign Out")
}
}
.opacity(model.isSignedIn ? 1 : 0)
//LoginView
VStack {
Text("Hello, Stranger")
Button(action: {
self.model.login()
}) {
Text("Sign In")
}
}
.opacity(model.isSignedIn ? 0 : 1)
}
.onReceive(self.model.$isSignedIn) { value in
withAnimation(Animation.easeIn) {
self.isSignedIn = value
}
}
}
}
There are some problems (in my opinion):
@State
var is required to handle changing in the modelWithAnimation
block requires separate .onReceive
So the question is: is it a correct way to apply WithAnimation
to @ObservedObject
, or is there a better solution?
You can specify the animation directly inside withAnimation
. This way it will be specific for this change only:
Button(action: {
withAnimation(.easeInOut) { // add animation
self.model.logout()
}
}) {
Text("Sign Out")
}