Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does an animation in a NavigationView starts from the top left corner?

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:
enter image description here

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?")
            }
        }
    }
}
like image 965
DanielZanchi Avatar asked Nov 21 '20 15:11

DanielZanchi


People also ask

What is NavigationView?

↳ 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 .

What is withAnimation SwiftUI?

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.

How do I use navigation in SwiftUI?

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.


3 Answers

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?")
                    }
            }
        }
    }
}
like image 83
Asperi Avatar answered Nov 15 '22 22:11

Asperi


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?")
            }
        }
    }
}
like image 20
DanielZanchi Avatar answered Nov 15 '22 23:11

DanielZanchi


You can wrap self.show = true with DispatchQueue.main.async. Like this:

.onAppear {
    DispatchQueue.main.async {
        self.show = true
    }
}
like image 22
lopes710 Avatar answered Nov 15 '22 22:11

lopes710