0

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)

animation jitter gif

Nevs12
  • 599
  • 6
  • 13
  • Is Home.speed lower (and how much) than ContentView.speed? I used 0.2 and it looks fine for me. Xcode 13.4 / iOS 15.5. – Asperi Jun 03 '22 at 05:45
  • @Asperi speed is the same. Sorry, in this example it should've been `ContentView.speed` instead of `Home.speed`, my mistake. Yep, at lower speeds jitters are much harder to notice, with 0.2 I barely noticing one lag at around ~8 o'clock. But still, would be great to know how to make with 0.5 or 1 for example. Will try to get video of what I mean. – Nevs12 Jun 03 '22 at 08:27
  • If you set animation duration same or longer than speed then they will overlay, that's probably what you observed. IMO it must be definitely remarkably less than time interval. – Asperi Jun 03 '22 at 08:45
  • If I understand the doc TimelineView redraw the contents at specific time intervals. In your case all the view is redrawn so animation may not be very smooth . You need to setup a transition to enable smooth switching between 2 images. You have 2 animation which may be Interfering : TimelineView and circle animation. – Ptit Xav Jun 06 '22 at 12:46

0 Answers0