如何创建像动画视图一样的无限渐变加载?

How to create an indefinite gradient loading like animated view?

我想创建一个带有渐变和无限加载的视图,如动画(渐变的最暗部分将从视图的左侧开始,然后用 2 秒向右动画,并且随着它“退出”右侧的视图,它会再次出现在左侧)。

一个视觉示例(不是动画,抱歉):

这是我开始写的,但它并不像我想要的那样工作。

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()
            }
        }
    }
}

使用:

VStack {
    Spacer()
    TestGradientView()
        .cornerRadius(4.0)
        .padding(.horizontal, 16)
        .frame(height: 8)
    Spacer()
}

动画需要一些小数数据,所以二进制开关实际上不起作用 - 它只是一个开关。

一种可能的方法是构造位置渐变,让动画知道渐变中的某些颜色位置发生了变化,因此可以动画化。这可以通过 AnimatableModifier.

来完成

这是一个演示 (Xcode 13.4 / iOS 15.5)

主要部分:

    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
    )

*常量可根据需要调整

Complete test module is here