随着时间的推移,重复的 SwiftUI 动画变得不稳定

Posted

技术标签:

【中文标题】随着时间的推移,重复的 SwiftUI 动画变得不稳定【英文标题】:Repeating SwiftUI animation gets choppy over time 【发布时间】:2020-06-05 06:20:21 【问题描述】:

我开发了一个重复的动画,它一开始很流畅,但在 10 或 20 秒后开始变得非常不稳定,在我的设备上什么都不做。这是视图的完整代码(设置为单视图应用的内容视图):

import SwiftUI

struct MovingCirclesView: View 
    @State var animationPercent: Double = 0

    var body: some View 
        ZStack 
            MovingCircle(animationPercent: $animationPercent, size: 300, movementRadius: 100, startAngle: 0)
                .offset(x: -200, y: -200)

            MovingCircle(animationPercent: $animationPercent, size: 400, movementRadius: 120, startAngle: .pi * 3/4)
                .offset(x: 50, y: 300)

            MovingCircle(animationPercent: $animationPercent, size: 350, movementRadius: 200, startAngle: .pi * 5/4)
                .offset(x: 10, y: 30)

            MovingCircle(animationPercent: $animationPercent, size: 230, movementRadius: 80, startAngle: .pi * 1/2)
                .offset(x: 220, y: -300)

            MovingCircle(animationPercent: $animationPercent, size: 230, movementRadius: 150, startAngle: .pi)
                .offset(x: 220, y: 100)
        .frame(maxWidth: .infinity, maxHeight: .infinity)
            .animation(Animation.linear(duration: 30).repeatForever(autoreverses: false))
            .onAppear()  self.animationPercent = 1 
    

    private struct MovingCircle: View, Animatable 
        @Binding var animationPercent: Double

        let size: CGFloat
        let movementRadius: CGFloat
        let startAngle: Double

        var body: some View 
            Circle()
                .frame(width: size, height: size)
                .foregroundColor(Color.white)
                .opacity(0.1)
                .offset(angle: .radians(.pi * 2 * self.animationPercent + self.startAngle), radius: self.movementRadius)
        
    


struct MovingCirclesView_Previews: PreviewProvider 
    static var previews: some View 
        MovingCirclesView()
            .background(Color.black)
            .edgesIgnoringSafeArea(.all)
    


struct AngularOffset: AnimatableModifier 
    var angle: Angle
    var radius: CGFloat

    var animatableData: AnimatablePair<Double, CGFloat> 
        get 
            return AnimatablePair(angle.radians, radius)
        
        set 
            self.angle = .radians(newValue.first)
            self.radius = newValue.second
        
    

    func body(content: Content) -> some View 
        return content.offset(CGSize(
            width: CGFloat(cos(self.angle.radians)) * self.radius,
            height: CGFloat(sin(self.angle.radians)) * self.radius
        ))
    

extension View 
    func offset(angle: Angle, radius: CGFloat) -> some View 
        ModifiedContent(content: self, modifier: AngularOffset(angle: angle, radius: radius))
    

总的想法是有一系列半透明的圆圈在圆圈中缓慢移动。我担心这不值得使用能源,并且无论如何都计划进行配置,但令我惊讶的是,动画似乎很快就陷入了困境。我在 iPhone X 上对其进行了分析,随着时间的推移,随着动画变得越来越不稳定,我没有看到 CPU 使用率和内存使用率有任何增加。

有没有人知道为什么动画会变得断断续续?我能做些什么来解决这个问题,还是我必须放弃这个想法?

【问题讨论】:

由于错过了.offset(angle:修饰符而无法编译。 @Asperi 哎呀,对不起,我忘了我使用了那个修饰符。我更新了我的帖子以包含该修饰符代码。 【参考方案1】:

这是一个解决方案 - 通过使用 .drawingGroup 并使用显式动画来激活 Metal。

在 Xcode 11.4 / ios 13.4 上运行良好 - 在 5 分钟内测试,CPU 负载 4-5%

ZStack 

   // ..  circles here

.frame(maxWidth: .infinity, maxHeight: .infinity)
.drawingGroup()
.onAppear() 
    withAnimation(Animation.linear(duration: 30).repeatForever(autoreverses: false)) 
        self.animationPercent = 1
    

注意:发现 - 在这种情况下,隐式动画似乎一次又一次地重新创建更新堆栈,因此它们成倍增加,但显式动画仅激活一次。

【讨论】:

有趣,当它是主要视图时,这确实解决了问题!我肯定需要更多地了解 SwiftUI 的内部工作原理。不幸的是,当我将此视图嵌入到更复杂的层次结构中时,动画会中断。它似乎遵循父动画(非常快速且仅一次)。关于如何解决这个问题的任何想法? (我尝试将其包装在另一个 ZStack with .animation(nil) 中,并在延迟后调用 withAnimation) 原来是因为我在另一个视图层次结构中的视图本身上明确设置了动画。幸运的是,似乎不需要显式动画,所以我能够将其删除并解决问题。 @drewag 我遇到了同样的问题。我尝试将隐式动画更改为显式动画,但没有运气。您能否更具体地解释一下您是如何解决此问题的? “幸运的是,似乎不需要显式动画,所以我能够将其删除并解决问题”这是什么意思?

以上是关于随着时间的推移,重复的 SwiftUI 动画变得不稳定的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的应用程序随着时间的推移变得反应迟钝?

随着时间的推移,使用 java websockets 实时流式传输模拟视频变得无响应

随着时间的推移重复条目计数不同

SwiftUI 动画:如何延迟重复动画

Python随着时间的推移在Scatter3d中绘制动画标记样式或点

如何摆脱不需要的 SwiftUI 动画?