@@ -3,13 +3,24 @@ import SwiftUI
33struct CircularProgressView : View {
44let value : Float ?
55
6- var strokeWidth : CGFloat = 4
7- var diameter : CGFloat = 22
6+ var strokeWidth : CGFloat
7+ var diameter : CGFloat
88var primaryColor : Color = . secondary
99var backgroundColor : Color = . secondary. opacity ( 0.3 )
1010
11- var autoCompleteThreshold : Float ?
12- var autoCompleteDuration : TimeInterval ?
11+ private var autoComplete : ( threshold: Float , duration: TimeInterval ) ?
12+ private var autoStart : ( until: Float , duration: TimeInterval ) ?
13+
14+ @State private var currentProgress : Float = 0
15+
16+ init ( value: Float ? = nil ,
17+ strokeWidth: CGFloat = 4 ,
18+ diameter: CGFloat = 22 )
19+ {
20+ self . value= value
21+ self . strokeWidth= strokeWidth
22+ self . diameter= diameter
23+ }
1324
1425var body : some View {
1526ZStack {
@@ -19,13 +30,23 @@ struct CircularProgressView: View {
1930. stroke ( backgroundColor, style: StrokeStyle ( lineWidth: strokeWidth, lineCap: . round) )
2031
2132Circle ( )
22- . trim ( from: 0 , to: CGFloat ( displayValue ( for: value ) ) )
33+ . trim ( from: 0 , to: CGFloat ( displayValue ( for: currentProgress ) ) )
2334. stroke ( primaryColor, style: StrokeStyle ( lineWidth: strokeWidth, lineCap: . round) )
2435. rotationEffect ( . degrees( - 90 ) )
25- . animation ( autoCompleteAnimation ( for: value) , value: value)
2636}
2737. frame ( width: diameter, height: diameter)
28-
38+ . onAppear {
39+ if let autoStart, value== 0 {
40+ withAnimation ( . easeOut( duration: autoStart. duration) ) {
41+ currentProgress= autoStart. until
42+ }
43+ }
44+ }
45+ . onChange ( of: value) {
46+ withAnimation ( currentAnimation ( for: value) ) {
47+ currentProgress= value
48+ }
49+ }
2950} else {
3051IndeterminateSpinnerView (
3152 diameter: diameter,
@@ -40,31 +61,39 @@ struct CircularProgressView: View {
4061}
4162
4263private func displayValue( for value: Float ) -> Float {
43- if let threshold= autoCompleteThreshold ,
64+ if let threshold= autoComplete ? . threshold ,
4465 value>= threshold, value< 1.0
4566{
4667return 1.0
4768}
4869return value
4970}
5071
51- private func autoCompleteAnimation( for value: Float ) -> Animation ? {
52- guard let threshold= autoCompleteThreshold,
53- let duration= autoCompleteDuration,
54- value>= threshold, value< 1.0
72+ private func currentAnimation( for value: Float ) -> Animation {
73+ guard let autoComplete,
74+ value>= autoComplete. threshold, value< 1.0
5575else {
76+ // Use the auto-start animation if it's running, otherwise default.
77+ if let autoStart{
78+ return . easeOut( duration: autoStart. duration)
79+ }
5680return . default
5781}
5882
59- return . easeOut( duration: duration)
83+ return . easeOut( duration: autoComplete . duration)
6084}
6185}
6286
6387extension CircularProgressView {
6488func autoComplete( threshold: Float , duration: TimeInterval ) -> CircularProgressView {
6589var view = self
66- view. autoCompleteThreshold= threshold
67- view. autoCompleteDuration= duration
90+ view. autoComplete= ( threshold: threshold, duration: duration)
91+ return view
92+ }
93+
94+ func autoStart( until value: Float , duration: TimeInterval ) -> CircularProgressView {
95+ var view = self
96+ view. autoStart= ( until: value, duration: duration)
6897return view
6998}
7099}