I would like to create a view with a gradient and an infinite loading like animation (the darkest part of the gradient would start from the left of the view, then take 2 seconds to animate to the right, and as it "exits" the view on the right, it would appear again on its left).
A visual example (not animated, sorry) :
Here is what I started writing but it doesn't work as I want.
struct TestGradientView: View {
@State private var animateGradient = false
var body: some View {
LinearGradient(
colors: [
.gray.opacity(0.25),
.gray.opacity(0.5)
],
startPoint: animateGradient ? .leading : .trailing,
endPoint: !animateGradient ? .leading : .trailing
)
.onAppear {
withAnimation(
.linear(duration: 2.0)
.repeatForever(autoreverses: true)
) {
animateGradient.toggle()
}
}
}
}
Use:
VStack {
Spacer()
TestGradientView()
.cornerRadius(4.0)
.padding(.horizontal, 16)
.frame(height: 8)
Spacer()
}
Thank you for your help!
CodePudding user response:
Animation needs some fractional data, so just binary switch actually does not work - it is just a toggle.
A possible approach is to construct positional gradient and let animation know that some of color position in gradient is changed, so animatable. This can be done with AnimatableModifier
.
Here is a demo (Xcode 13.4 / iOS 15.5)
Main part:
RoundedRectangle(cornerRadius: 12).fill(.clear)
.modifier(GradientProgressEffect(position: animate))
.animation(.linear(duration: 2.0)
.repeatForever(autoreverses: true), value: animate)
.onAppear {
animate = 0.9
}
// ...
LinearGradient(
stops: [
.init(color: .gray.opacity(0.1), location: 0.0),
.init(color: .gray.opacity(1), location: position - 0.05),
.init(color: .gray.opacity(1), location: position 0.05),
.init(color: .gray.opacity(0.1), location: 1.0),
],
startPoint: .leading,
endPoint: .trailing
)
*constants can be fit per needs