omattyao
omattyao

Reputation: 95

How to combine rotation and fade-out animation in SwiftUI

My screen is showing a rotating indicator until the data to be displayed finishes downloading.

I was able to implement the indicator rotation animation correctly.

struct MainView: View {

    @ObservedObject var viewModel = MainViewModel()

    var body: some View {
        VStack(spacing: 0) {
            if let data = viewModel.fetchedData {
                Text(data.description)
            } else {
                Spacer()
            }
            AdBanner()
        }
        .overlay(Indicator(shown: $viewModel.isLoading))
    }
}

struct Indicator: View {

    @Binding var shown: Bool
    @State private var rotating = false

    @ViewBuilder
    var body: some View {
        if shown {
            Image("Ring")
                .rotationEffect(.degrees(rotating ? 360 : 0))
                .onAppear {
                    withAnimation(.linear(duration: 1).repeatForever(autoreverses: false) {
                        self.rotating = true
                    }
                }
        }
    }
}

When the data download is finished, I want this indicator to fade out with its rotation continuing. I have tried many things, but I cannot implement this correctly. For example, it may not animate, or it may fade out but the rotation may be broken.

before:

        .overlay(Indicator(shown: $viewModel.isLoading))

after:

        .overlay(Indicator(shown: $viewModel.isLoading.animation(.easeOut(duration: 1))))

before:

        if shown {
            Image("Ring")
                .rotationEffect(.degrees(rotating ? 360 : 0))
                .onAppear {
                    withAnimation {
                        self.rotating = true
                    }
                }
        }

after:

    ZStack {
        if shown {
            Image("Ring")
                .rotationEffect(rotating ? 360 : 0)
                .onAppear {
                    withAnimation {
                        self.rotating = true
                    }
                }
        }
    }
    .transition(.opacity)
    .animation(.easeOut(duration: 1))

Is it possible to fade out the rotation animation without interrupting it?

Upvotes: 1

Views: 3258

Answers (1)

Asperi
Asperi

Reputation: 258443

You need transition, because view (image in this case) is removed from view hierarchy. And transition is animated by container of removing view.

Note: it is better to link every animation to own switching state to avoid affect on other animations

Here is solution. Tested with Xcode 12.1 / iOS 14.1

struct Indicator: View {
    
    @Binding var shown: Bool
    @State private var rotating = false
    
    @ViewBuilder
    var body: some View {
        VStack {
            if shown {
                Image("Ring")
                    .rotationEffect(Angle(degrees: rotating ? 360 : 0))
                    .animation(Animation.linear(duration: 1).repeatForever(autoreverses: false), value: rotating)
                    .transition(.opacity)
                    .onAppear {
                        self.rotating = true
                    }
            }
        }.animation(.default, value: shown)
    }
}

Upvotes: 2

Related Questions