@@ -3,13 +3,24 @@ import SwiftUI
3
3
struct CircularProgressView : View {
4
4
let value : Float ?
5
5
6
- var strokeWidth : CGFloat = 4
7
- var diameter : CGFloat = 22
6
+ var strokeWidth : CGFloat
7
+ var diameter : CGFloat
8
8
var primaryColor : Color = . secondary
9
9
var backgroundColor : Color = . secondary. opacity ( 0.3 )
10
10
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
+ }
13
24
14
25
var body : some View {
15
26
ZStack {
@@ -19,13 +30,23 @@ struct CircularProgressView: View {
19
30
. stroke ( backgroundColor, style: StrokeStyle ( lineWidth: strokeWidth, lineCap: . round) )
20
31
21
32
Circle ( )
22
- . trim ( from: 0 , to: CGFloat ( displayValue ( for: value ) ) )
33
+ . trim ( from: 0 , to: CGFloat ( displayValue ( for: currentProgress ) ) )
23
34
. stroke ( primaryColor, style: StrokeStyle ( lineWidth: strokeWidth, lineCap: . round) )
24
35
. rotationEffect ( . degrees( - 90 ) )
25
- . animation ( autoCompleteAnimation ( for: value) , value: value)
26
36
}
27
37
. 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
+ }
29
50
} else {
30
51
IndeterminateSpinnerView (
31
52
diameter: diameter,
@@ -40,31 +61,39 @@ struct CircularProgressView: View {
40
61
}
41
62
42
63
private func displayValue( for value: Float ) -> Float {
43
- if let threshold = autoCompleteThreshold ,
64
+ if let threshold = autoComplete ? . threshold ,
44
65
value >= threshold, value < 1.0
45
66
{
46
67
return 1.0
47
68
}
48
69
return value
49
70
}
50
71
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
55
75
else {
76
+ // Use the auto-start animation if it's running, otherwise default.
77
+ if let autoStart {
78
+ return . easeOut( duration: autoStart. duration)
79
+ }
56
80
return . default
57
81
}
58
82
59
- return . easeOut( duration: duration)
83
+ return . easeOut( duration: autoComplete . duration)
60
84
}
61
85
}
62
86
63
87
extension CircularProgressView {
64
88
func autoComplete( threshold: Float , duration: TimeInterval ) -> CircularProgressView {
65
89
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)
68
97
return view
69
98
}
70
99
}
0 commit comments