@@ -3,13 +3,24 @@ import SwiftUI
33struct CircularProgressView : View {
44 let value : Float ?
55
6- var strokeWidth : CGFloat = 4
7- var diameter : CGFloat = 22
6+ var strokeWidth : CGFloat
7+ var diameter : CGFloat
88 var primaryColor : Color = . secondary
99 var 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
1425 var body : some View {
1526 ZStack {
@@ -19,13 +30,23 @@ struct CircularProgressView: View {
1930 . stroke ( backgroundColor, style: StrokeStyle ( lineWidth: strokeWidth, lineCap: . round) )
2031
2132 Circle ( )
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 {
3051 IndeterminateSpinnerView (
3152 diameter: diameter,
@@ -40,31 +61,39 @@ struct CircularProgressView: View {
4061 }
4162
4263 private func displayValue( for value: Float ) -> Float {
43- if let threshold = autoCompleteThreshold ,
64+ if let threshold = autoComplete ? . threshold ,
4465 value >= threshold, value < 1.0
4566 {
4667 return 1.0
4768 }
4869 return 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
5575 else {
76+ // Use the auto-start animation if it's running, otherwise default.
77+ if let autoStart {
78+ return . easeOut( duration: autoStart. duration)
79+ }
5680 return . default
5781 }
5882
59- return . easeOut( duration: duration)
83+ return . easeOut( duration: autoComplete . duration)
6084 }
6185}
6286
6387extension CircularProgressView {
6488 func autoComplete( threshold: Float , duration: TimeInterval ) -> CircularProgressView {
6589 var 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)
6897 return view
6998 }
7099}
0 commit comments