swiftuizstack

Zstack alignment issue on NavigationView


Two circles on Zstack is not align if view is render on NavigationView.

struct SpinnerView: View {
 
    @State private var isLoading = false
 
    var body: some View {
        NavigationView {
            ZStack {
                
                Circle()
                    .stroke(Color(.systemGray5), lineWidth: 7)
                    .frame(width: 64, height: 64)
                
                Circle()
                    .trim(from: 0, to: 0.2)
                    .stroke(Color.green, lineWidth: 7)
                    .frame(width: 64, height: 64)
                
                    .rotationEffect(Angle(degrees: isLoading ? 360 : 0))
                    .animation(Animation.linear(duration: 1).repeatForever(autoreverses: false))
                    .onAppear() {
                        self.isLoading = true
                    }
            }
        }
    }
}

enter image description here


Solution

  • In order to reproduce your problem, I moved NavigationView to a start screen ContentView and launch SpinnerView with a NavigationLink. Once I do that, I can see the falling animation.

    If you turn the animation into an explicit animation triggered by withAnimation(), and delay it until the view loads, you can avoid the extra movement introduced by the NavigationView. By changing isLoading to true initially, and then setting it to false in onAppear before calling withAnimation, the false state which occurs after the view is loaded will be established (for the purposes of the animation) as the before state. Then, when isLoading is set to true in the withAnimation block, the animation will proceed. This avoids including the movement caused by imbedding the screen in a navigation screen because this all happens before isLoading is set to false.

    struct SpinnerView: View {
        
        @State private var isLoading = true
        
        var body: some View {
            ZStack {
                Circle()
                    .stroke(Color(.systemGray5), lineWidth: 7)
                    .frame(width: 64, height: 64)
                
                Circle()
                    .trim(from: 0, to: 0.2)
                    .stroke(Color.green, lineWidth: 7)
                    .frame(width: 64, height: 64)
                    .rotationEffect(Angle(degrees: isLoading ? 360 : 0))
                    .onAppear() {
                        self.isLoading = false
                        withAnimation(.linear(duration: 1).repeatForever(autoreverses: false)) {
                            self.isLoading = true
                        }
                    }
            }
        }
    }
    
    struct ContentView: View {
        var body: some View {
            NavigationView {
                NavigationLink("Go to spinner view", destination: SpinnerView())
            }
        }
    }
    

    Here is it running in the simulator:

    Demo running in simulator