I'm trying to create a simple animation in SwiftUI and I would like the animation to start from center top and end to the center of the screen.
But, as you can see in the video, the animation starts from the top left corner when I use a NavigationView:
This is the code I am using for this example:
struct ContentView: View {
@State var show = false
var body: some View {
NavigationView {
HStack {
Text("Hello, world!")
.padding()
.background(Color.blue)
.offset(x: 0, y: show ? 0 : -30)
.animation(Animation.easeOut.delay(0.6))
.onAppear {
self.show = true
}
.navigationTitle("Why?")
}
}
}
}
↳ com.google.android.material.navigation.NavigationView. Represents a standard navigation menu for application. The menu contents can be populated by a menu resource file. NavigationView is typically placed inside a DrawerLayout .
withAnimation() takes a parameter specifying the kind of animation you want, so you could create a three-second linear animation like this: withAnimation(.linear(duration: 3)) Explicit animations are often helpful because they cause every affected view to animate, not just those that have implicit animations attached.
SwiftUI's NavigationLink has a second initializer that has an isActive parameter, allowing us to read or write whether the navigation link is currently active. In practical terms, this means we can programmatically trigger the activation of a navigation link by setting whatever state it's watching to true.
Why does an animation in a NavigationView starts from the top left corner?
Because implicit animation affects all animatable properties, including position, which on creation time is CGPoint.zero
(ie. top-left corner).
Such cases are solved by linking animation to dependent state value, thus it activates only when state changed, so all animatable properties not affected.
Here is possible variant for your case. Tested with Xcode 12.1 / iOS 14.1.
struct ContentView: View {
@State var show = false
var body: some View {
GeometryReader { gp in
NavigationView {
HStack {
Text("Hello, world!")
.padding()
.background(Color.blue)
.offset(x: 0, y: show ? 0 : -gp.size.height / 2)
.animation(Animation.easeOut.delay(0.6), value: show)
.onAppear {
self.show = true
}
.navigationTitle("Why?")
}
}
}
}
}
So the solution is to use the value parameter on the animation and it works:
struct ContentView: View {
@State var show = false
var body: some View {
NavigationView {
HStack {
Text("Hello, world!")
.padding()
.background(Color.blue)
.offset(x: 0, y: show ? 0 : -120)
.animation(Animation.easeOut.delay(0.6), value: show)
.onAppear {
self.show = true
}
.navigationTitle("Why?")
}
}
}
}
You can wrap self.show = true with DispatchQueue.main.async. Like this:
.onAppear {
DispatchQueue.main.async {
self.show = true
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With