Home > front end >  How to create an indefinite gradient loading like animated view?
How to create an indefinite gradient loading like animated view?

Time:05-28

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) :

enter image description here

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)

enter image description here

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

Complete test module is here

  • Related