Reputation: 2714
I've created the code for displaying the pie progress bar. I need to add an animation to this progress bar. I tried linear animation but it didn't help. I'm stuck and don't know how to get it to animate. Can someone help? Here's the code.
import SwiftUI
struct PieProgress: View {
@State var progress: Float
var body: some View {
GeometryReader { geometry in
VStack(spacing:20) {
HStack{
Text("0%")
Slider(value: self.$progress)
Text("100%")
}.padding()
Circle()
.stroke(Color.gray, lineWidth: 1)
.frame(width: geometry.size.width, height: geometry.size.width, alignment: .center)
.padding()
.overlay(
PieShape(progress: Double(self.progress))
.frame(width: geometry.size.width - 10, height: geometry.size.width - 10 , alignment: .center)
.foregroundColor(.blue)
)
}
}
}
}
struct PieShape: Shape {
var progress: Double = 0.0
private let startAngle: Double = (Double.pi) * 1.5
private var endAngle: Double {
get {
return self.startAngle + Double.pi * 2 * self.progress
}
}
func path(in rect: CGRect) -> Path {
var path = Path()
let arcCenter = CGPoint(x: rect.size.width / 2, y: rect.size.width / 2)
let radius = rect.size.width / 2
path.move(to: arcCenter)
path.addArc(center: arcCenter, radius: radius, startAngle: Angle(radians: startAngle), endAngle: Angle(radians: endAngle), clockwise: false)
path.closeSubpath()
return path
}
}
Upvotes: 4
Views: 2974
Reputation: 257779
With updated deprecated methods and now w/o hard-codes
Circle()
.stroke(Color.gray, lineWidth: 1)
.overlay(
PieShape(progress: Double(self.progress))
.padding(4)
.foregroundColor(.blue)
)
.frame(maxWidth: .infinity)
.animation(Animation.linear, value: progress) // << here !!
.aspectRatio(contentMode: .fit)
Here is possible approach (tested & works with Xcode 11.2 / iOS 13.2):
You need to specify animatableData
for your shape as below
struct PieShape: Shape { var progress: Double = 0.0
var animatableData: Double {
get {
self.progress
}
set {
self.progress = newValue
}
}
...
then add animation to Circle
Circle() .stroke(Color.gray, lineWidth: 1) .frame(width: geometry.size.width, height: geometry.size.width, alignment: .center) .padding() .overlay( PieShape(progress: Double(self.progress)) .frame(width: geometry.size.width - 10, height: geometry.size.width - 10 , alignment: .center) .foregroundColor(.blue) ) .animation(Animation.linear) // << here !!
and that's it. For testing (only!) you can add the following in PieProgress
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
self.progress = 0.72
}
}
Note: to use PieProgress
as reused component it would be reasonable to make progress
as @Binding
, just in case.
Upvotes: 3