Home > Software engineering >  Circular ProgressBar View does no longer animates with iOS15
Circular ProgressBar View does no longer animates with iOS15

Time:10-13

Using Swift5.5, iOS15.0.1,

I had to realise that my circular ProgressBar does no longer animate after updating to iOS15.

  1. Below is my code - can anybody tell me what to do in order to make the circular ProgressBar-View animate again ?

  2. Can anybody tell me how to circumvent in this example the deprecation-warning animation' was deprecated in iOS 15.0: Use withAnimation or animation(_:value:) instead. ?

import SwiftUI

struct ContentView: View {
    
    @State var progressValue: Float = 0.28
    
    var body: some View {
        ProgressBar(progress: $progressValue)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct ProgressBar: View {
    @Binding var progress: Float
    
    var body: some View {
        ZStack {
            Circle()
                .stroke(lineWidth: 20.0)
                .opacity(0.3)
                .foregroundColor(Color.red)
            
            Circle()
                .trim(from: 0.0, to: CGFloat(min(self.progress, 1.0)))
                .stroke(style: StrokeStyle(lineWidth: 20.0, lineCap: .round, lineJoin: .round))
                .foregroundColor(Color.red)
                .rotationEffect(Angle(degrees: 270.0))
                .animation(.linear)
            Text(String(format: "%.0f %%", min(self.progress, 1.0)*100.0))
                .font(.largeTitle)
                .bold()
        }
    }
}

My original version looked like this:

Circle()
.trim(from: 0, to: showFreespaceRing ? CGFloat(Double(freeDiskspace) / Double(totalDiskspace)) : 0)
.stroke(Color.green.opacity(0.7), style: StrokeStyle(lineWidth: 10, lineCap: .round))
.frame(width: circleDiam, height: circleDiam)
.animation(.easeIn(duration: 1))
.onAppear() {
    showFreespaceRing = true
}

CodePudding user response:

The value parameter takes an Equatable that represents the value that is animating. In this case : progress.

I also moved .animation outside of the ZStack -- otherwise, I was seeing a funny jitter on the animation.

struct ContentView: View {
    
    @State var progressValue: Float = 0.28
    
    let timer = Timer.publish(every: 1.0, on: .main, in: .default).autoconnect()
    
    var body: some View {
        ProgressBar(progress: $progressValue)
            .onReceive(timer) { _ in
                progressValue  = 0.1
            }
    }
}

struct ProgressBar: View {
    @Binding var progress: Float
    
    var body: some View {
        ZStack {
            Circle()
                .stroke(lineWidth: 20.0)
                .opacity(0.3)
                .foregroundColor(Color.red)
            
            Circle()
                .trim(from: 0.0, to: CGFloat(min(self.progress, 1.0)))
                .stroke(style: StrokeStyle(lineWidth: 20.0, lineCap: .round, lineJoin: .round))
                .foregroundColor(Color.red)
                .rotationEffect(Angle(degrees: 270.0))
            Text(String(format: "%.0f %%", min(self.progress, 1.0)*100.0))
                .font(.largeTitle)
                .bold()
        }
        .animation(.linear, value: progress)
    }
}

CodePudding user response:

I finally found out was went wrong:

On my original version, I had

Circle()
    .trim(from: 0, to: showFreespaceRing ? CGFloat(Double(freeDiskspace) / Double(totalDiskspace)) : 0)
    .stroke(Color.green.opacity(0.7), style: StrokeStyle(lineWidth: 10, lineCap: .round))
    .frame(width: circleDiam, height: circleDiam)
    .animation(.easeIn(duration: 1))
    .onAppear() {
        showFreespaceRing = true
    }

And now I have:

Circle()
    .trim(from: 0, to: showFreespaceRing ? 0 : CGFloat(Double(freeDiskspace) / Double(totalDiskspace)))
    .stroke(Color.green.opacity(0.7), style: StrokeStyle(lineWidth: 10, lineCap: .round))
    .frame(width: circleDiam, height: circleDiam)
    .animation(.easeIn(duration: 1), value: showFreespaceRing)
    .onAppear() {
        showFreespaceRing.toggle()
    }

With this, the animation works again (somewhat - see video below, there is still one issue)....

The trick was to use .toggle() inside the onAppear method.

What definitively does not work is to have showFreespaceRing = true inside the onAppear() method (but rather showFreespaceRing.toggle() instead !!!!!!!

And, of course, fulfilling iOS15's new value inside animation:

.animation(.easeIn(duration: 1), value: showFreespaceRing)

However, there is one annoyance, still, with the current solution:

THE ANIMATION IS NOT SMOOTH !!

See this video:

If you look carefully you can see that the animation is not smooth at all but rather flickering badly. (i.e. the ring-animation looks catastrophic, still). How can I get a smooth animation ??

enter image description here

  • Related