|
| 1 | +import SwiftUI |
| 2 | + |
| 3 | +structCircularProgressView:View{ |
| 4 | +letvalue:Float? |
| 5 | + |
| 6 | +varstrokeWidth:CGFloat=4 |
| 7 | +vardiameter:CGFloat=22 |
| 8 | +varprimaryColor:Color=.secondary |
| 9 | +varbackgroundColor:Color=.secondary.opacity(0.3) |
| 10 | + |
| 11 | +@Stateprivatevarrotation=0.0 |
| 12 | +@StateprivatevartrimAmount:CGFloat=0.15 |
| 13 | + |
| 14 | +varautoCompleteThreshold:Float? |
| 15 | +varautoCompleteDuration:TimeInterval? |
| 16 | + |
| 17 | +varbody:someView{ |
| 18 | +ZStack{ |
| 19 | + // Background circle |
| 20 | +Circle() |
| 21 | +.stroke(backgroundColor, style:StrokeStyle(lineWidth: strokeWidth, lineCap:.round)) |
| 22 | +.frame(width: diameter, height: diameter) |
| 23 | +Group{ |
| 24 | +iflet value{ |
| 25 | + // Determinate gauge |
| 26 | +Circle() |
| 27 | +.trim(from:0, to:CGFloat(displayValue(for: value))) |
| 28 | +.stroke(primaryColor, style:StrokeStyle(lineWidth: strokeWidth, lineCap:.round)) |
| 29 | +.frame(width: diameter, height: diameter) |
| 30 | +.rotationEffect(.degrees(-90)) |
| 31 | +.animation(autoCompleteAnimation(for: value), value: value) |
| 32 | +}else{ |
| 33 | + // Indeterminate gauge |
| 34 | +Circle() |
| 35 | +.trim(from:0, to: trimAmount) |
| 36 | +.stroke(primaryColor, style:StrokeStyle(lineWidth: strokeWidth, lineCap:.round)) |
| 37 | +.frame(width: diameter, height: diameter) |
| 38 | +.rotationEffect(.degrees(rotation)) |
| 39 | +} |
| 40 | +} |
| 41 | +} |
| 42 | +.frame(width: diameter+ strokeWidth*2, height: diameter+ strokeWidth*2) |
| 43 | +.onAppear{ |
| 44 | +if value==nil{ |
| 45 | +withAnimation(.linear(duration:0.8).repeatForever(autoreverses:false)){ |
| 46 | + rotation=360 |
| 47 | +} |
| 48 | +} |
| 49 | +} |
| 50 | +} |
| 51 | + |
| 52 | +privatefunc displayValue(for value:Float)->Float{ |
| 53 | +iflet threshold= autoCompleteThreshold, |
| 54 | + value>= threshold, value<1.0 |
| 55 | +{ |
| 56 | +return1.0 |
| 57 | +} |
| 58 | +return value |
| 59 | +} |
| 60 | + |
| 61 | +privatefunc autoCompleteAnimation(for value:Float)->Animation?{ |
| 62 | +guardlet threshold= autoCompleteThreshold, |
| 63 | +let duration= autoCompleteDuration, |
| 64 | + value>= threshold, value<1.0 |
| 65 | +else{ |
| 66 | +return.default |
| 67 | +} |
| 68 | + |
| 69 | +return.easeOut(duration: duration) |
| 70 | +} |
| 71 | +} |
| 72 | + |
| 73 | +extensionCircularProgressView{ |
| 74 | +func autoComplete(threshold:Float, duration:TimeInterval)->CircularProgressView{ |
| 75 | +varview=self |
| 76 | + view.autoCompleteThreshold= threshold |
| 77 | + view.autoCompleteDuration= duration |
| 78 | +return view |
| 79 | +} |
| 80 | +} |