随时间变化矩形的动画效果

时间:2019-12-05 03:27:29

标签: animation swiftui watchos

在WatchOS上的SwiftUI中,如何动画矩形(或与此相关的任何视图)的宽度,以使其开始于某个值并在指定的时间动画为另一个值?

具体来说,我想给“矩形”设置动画以指示到下一整分钟或一分钟后接下来的30秒的剩余时间。

我所看到的所有示例都是基于Timer.scheduledTimer以较高的速度触发并设置@State变量,但是我的理解是,尤其是在WatchOS上,应避免这种情况。有更好的方法吗?

这是我拥有的基于计时器/状态的代码,但我觉得应该有一种更有效的方法:

import SwiftUI

func percentage() -> CGFloat {
    1 - CGFloat(fmod(Date().timeIntervalSince1970, 30) / 30)
}


struct ContentView: View {
    @State var ratio: CGFloat = percentage()

    let timer = Timer.publish(every: 1 / 60, on:.main, in:.common).autoconnect()

    var body: some View {
        GeometryReader { geometry in
            ZStack {
                Rectangle()
                    .foregroundColor(Color.gray)
                    .frame(width:geometry.size.width, height:5)
                HStack {
                    Rectangle()
                        .foregroundColor(Color.red)
                        .frame(width:geometry.size.width * self.ratio, height:5)
                    Spacer()
                }
            }
        }.onReceive(self.timer) { _ in
            self.ratio = percentage()
        }
    }
}

1 个答案:

答案 0 :(得分:1)

我认为使用动画的“更有效的方式”:

struct AnimationRectangle: View {

    struct AnimationRectangle: View {

    @State private var percentage: CGFloat = 0.0
    // count, how much time left to nearest 30 seconds
    @State private var animationDuration = 30 - Double(fmod(Date().timeIntervalSince1970, 30)) 
    private var repeatedAnimationFor30Seconds: Animation {
        return Animation.easeInOut(duration: 30)
            .repeatForever(autoreverses: false)
    }

    var body: some View {

        VStack {

            // just showing duration of current animation
            Text("\(self.animationDuration)")

            ZStack {
                Rectangle()
                    .foregroundColor(.gray)

                GeometryReader { geometry in
                    HStack {
                        Rectangle()
                            .foregroundColor(.green)
                            .frame(width: geometry.size.width * self.percentage)

                        Spacer()
                    }
                }


            }
            .frame(height: 5)
            .onAppear() {

                // first animation without repeating
                withAnimation(Animation.easeInOut(duration: self.animationDuration)) {
                    self.percentage = 1.0
                }

                // other repeated animations
                DispatchQueue.main.asyncAfter(deadline: .now() + self.animationDuration) {
                    self.percentage = 0.0
                    self.animationDuration = 30.0
                    withAnimation(self.repeatedAnimationFor30Seconds) {
                        self.percentage = 1.0
                    }
                }

            }
        }

    }
}
struct AnimationRectangle_Previews: PreviewProvider {
    static var previews: some View {
        AnimationRectangle()
    }
}