Struggling a bit to achieve perfect smoothness of animation in SwiftUI. All suggestions are welcomed.
Example: stopwatch. Pretty basic setup of animating progress of something using simple shape
and trim
.
Note: code is simplified, e.g. @State private var progress: CGFloat
can be an observable value from a viewModel
. Point is that periodic state updates should be smoothly animated without any noticeable lag or jitter.
struct ContentView: View {
static let speed: TimeInterval = 0.5
var body: some View {
TimelineView(.periodic(from: .now, by: Self.speed)) { (context) in
Stopwatch(date: context.date)
.padding()
}
}
}
struct Stopwatch: View {
@State private var progress: CGFloat = 0
let date: Date
var body: some View {
Circle()
.trim(from: 0, to: progress)
.stroke(style: StrokeStyle(lineWidth: 5, lineCap: .round, lineJoin: .round))
.foregroundColor(.green)
.animation(.linear(duration: ContentView.speed), value: progress)
.onChange(of: date) { (_) in
if progress == 1 {
progress = 0
}
else {
progress += 0.2
}
}
}
}
If you run the example on Simulator or on Device, you will be able to sometimes notice a small annoying jitter in animation if you follow the line with your eyes closely.
Unfortunately, GIF is not conveying what the problem actually is. Therefore, I encourage you to try it in xCode to see. For me, when I follow the line, I notice small jitter at ~6, ~11 and at ~13 o'clock. Seems like animation FPS is a bit low and in these points it dips even more. Any ideas how to make it more smooth? :)
PS: Jitter is also noticeable even without periodic state updates. Problem can be noticed (though arguably a bit less pronounced) by simply making:
Circle()
.trim(from: 0, to: animating ? 1 : 0)
.stroke(style: StrokeStyle(lineWidth: 5, lineCap: .round, lineJoin: .round))
.foregroundColor(.green)
.animation(.linear(duration: 3)
.repeatForever(autoreverses: true), value: animating)