通过简单的 swiftui 动画,CPU 使用率会随着时间的推移而增加

问题描述 投票:0回答:1

我创建了一个简单的改变大小的圆圈动画。圆圈在屏幕中央像心跳一样跳动。我的预期是 CPU 使用率非常低,但事实并非如此。此外,即使 CPU 使用率没有我预期的那么低,我也不认为 CPU 使用率会随着时间的推移而增加,因为应用程序中没有发生任何其他事情。这是代码:

import SwiftUI

@main
struct TestApp: App {

  var body: some Scene {
    WindowGroup {
      SplashScreenView()
    }
  }

}
import SwiftUI

struct SplashScreenView: View {

  var body: some View {
    ZStack {
      SplashNucleusView(minSize: 50, maxSize: 100)
    }
  }

}
import SwiftUI

struct SplashNucleusView: View {
  let minSize: Double
  let maxSize: Double

  @State private var nucleusColor: Color = .primary
  @State private var nucleusRadius: Double = 10
  @State private var nucleusOpacity: Double = 1.0

  private var nucleusAnimation: Animation {
    .easeInOut(duration: 0.25)
    .repeatForever(autoreverses: true)
  }

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

  var body: some View {
    Circle()
      .fill(nucleusColor)
      .frame(width: nucleusRadius)
      .opacity(nucleusOpacity)
      .onReceive(timer) { _ in
        withAnimation(nucleusAnimation) {
          nucleusRadius = Double.random(in: minSize...maxSize)
        }
      }
  }

}

这就是动画的样子:

Pulsing circle

动画一直很流畅,直到 CPU 使用率达到 95%,此时会出现明显的卡顿。以下是当动画持续时间值为 0.5 秒且计时器发布间隔为 3 秒时 CPU 的情况:

Graph

将动画持续时间值更改为 0.25 秒,并将计时器发布间隔更改为 0.5 秒,CPU 使用率会发生如下变化:

Graph

最终的动画还有许多其他移动部分,这使问题变得更糟。仅用圆形视图即可明显看出该问题。我花了几个小时使用调试器、阅读动画并测试新方法,但结果总是相同的。

为什么会出现这种情况?

animation swiftui
1个回答
0
投票

let timer = Timer...
实际上对于 SwiftUI 来说并不是一个好的实践,你最终可能会出现内存泄漏,因为 SwiftUI 可以随时重新创建视图并创建多个计时器。

处理动画的“最佳”方法就是使用

TimelineView

TimelineView(.periodic(from: startDate, by: 1)) { context in
     AnalogTimerView(date: context.date)
}

https://developer.apple.com/documentation/swiftui/timelineview

但是如果你必须使用

Timer
,你应该将它与
onReceive
一起使用,这样它就会被适当地销毁。

 .onReceive(Timer.publish(every: 1, on: .main, in: .common).autoconnect()) { output in
    print("Timer called")
}

如果您也可以进入异步/等待并开始使用

Task
作为替代方案。

struct TimerSample: View {
    @State private var isDisabled: Bool = true
    var body: some View {
        Button("Hello World!") {
            
        }.disabled(isDisabled)
            .task(id: isDisabled) {
                guard isDisabled else {return}
                try? await Task.sleep(for: .seconds(2))
                isDisabled.toggle()
            }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.