我创建了一个简单的改变大小的圆圈动画。圆圈在屏幕中央像心跳一样跳动。我的预期是 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)
}
}
}
}
这就是动画的样子:
动画一直很流畅,直到 CPU 使用率达到 95%,此时会出现明显的卡顿。以下是当动画持续时间值为 0.5 秒且计时器发布间隔为 3 秒时 CPU 的情况:
将动画持续时间值更改为 0.25 秒,并将计时器发布间隔更改为 0.5 秒,CPU 使用率会发生如下变化:
最终的动画还有许多其他移动部分,这使问题变得更糟。仅用圆形视图即可明显看出该问题。我花了几个小时使用调试器、阅读动画并测试新方法,但结果总是相同的。
为什么会出现这种情况?
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()
}
}
}