Reputation: 95
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
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