@@ -8,45 +8,35 @@ struct CircularProgressView: View {
88 var primaryColor : Color = . secondary
99 var backgroundColor : Color = . secondary. opacity ( 0.3 )
1010
11- @State private var rotation = 0.0
12- @State private var trimAmount : CGFloat = 0.15
13-
1411 var autoCompleteThreshold : Float ?
1512 var autoCompleteDuration : TimeInterval ?
1613
1714 var body : some View {
1815 ZStack {
19- // Background circle
20- Circle ( )
21- . stroke ( backgroundColor, style: StrokeStyle ( lineWidth: strokeWidth, lineCap: . round) )
22- . frame ( width: diameter, height: diameter)
23- Group {
24- if let value {
25- // Determinate gauge
16+ if let value {
17+ ZStack {
18+ Circle ( )
19+ . stroke ( backgroundColor, style: StrokeStyle ( lineWidth: strokeWidth, lineCap: . round) )
20+
2621 Circle ( )
2722 . trim ( from: 0 , to: CGFloat ( displayValue ( for: value) ) )
2823 . stroke ( primaryColor, style: StrokeStyle ( lineWidth: strokeWidth, lineCap: . round) )
29- . frame ( width: diameter, height: diameter)
3024 . rotationEffect ( . degrees( - 90 ) )
3125 . 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) )
3926 }
27+ . frame ( width: diameter, height: diameter)
28+
29+ } else {
30+ IndeterminateSpinnerView (
31+ diameter: diameter,
32+ strokeWidth: strokeWidth,
33+ primaryColor: NSColor ( primaryColor) ,
34+ backgroundColor: NSColor ( backgroundColor)
35+ )
36+ . frame ( width: diameter, height: diameter)
4037 }
4138 }
4239 . 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- }
5040 }
5141
5242 private func displayValue( for value: Float ) -> Float {
@@ -78,3 +68,55 @@ extension CircularProgressView {
7868 return view
7969 }
8070}
71+
72+ // We note a constant >10% CPU usage when using a SwiftUI rotation animation that
73+ // repeats forever, while this implementation, using Core Animation, uses <1% CPU.
74+ struct IndeterminateSpinnerView : NSViewRepresentable {
75+ var diameter : CGFloat
76+ var strokeWidth : CGFloat
77+ var primaryColor : NSColor
78+ var backgroundColor : NSColor
79+
80+ func makeNSView( context _: Context ) -> NSView {
81+ let view = NSView ( frame: NSRect ( x: 0 , y: 0 , width: diameter, height: diameter) )
82+ view. wantsLayer = true
83+
84+ guard let viewLayer = view. layer else { return view }
85+
86+ let fullPath = NSBezierPath (
87+ ovalIn: NSRect ( x: 0 , y: 0 , width: diameter, height: diameter)
88+ ) . cgPath
89+
90+ let backgroundLayer = CAShapeLayer ( )
91+ backgroundLayer. path = fullPath
92+ backgroundLayer. strokeColor = backgroundColor. cgColor
93+ backgroundLayer. fillColor = NSColor . clear. cgColor
94+ backgroundLayer. lineWidth = strokeWidth
95+ viewLayer. addSublayer ( backgroundLayer)
96+
97+ let foregroundLayer = CAShapeLayer ( )
98+
99+ foregroundLayer. frame = viewLayer. bounds
100+ foregroundLayer. path = fullPath
101+ foregroundLayer. strokeColor = primaryColor. cgColor
102+ foregroundLayer. fillColor = NSColor . clear. cgColor
103+ foregroundLayer. lineWidth = strokeWidth
104+ foregroundLayer. lineCap = . round
105+ foregroundLayer. strokeStart = 0
106+ foregroundLayer. strokeEnd = 0.15
107+ viewLayer. addSublayer ( foregroundLayer)
108+
109+ let rotationAnimation = CABasicAnimation ( keyPath: " transform.rotation " )
110+ rotationAnimation. fromValue = 0
111+ rotationAnimation. toValue = 2 * Double. pi
112+ rotationAnimation. duration = 1.0
113+ rotationAnimation. repeatCount = . infinity
114+ rotationAnimation. isRemovedOnCompletion = false
115+
116+ foregroundLayer. add ( rotationAnimation, forKey: " rotationAnimation " )
117+
118+ return view
119+ }
120+
121+ func updateNSView( _: NSView , context _: Context ) { }
122+ }
0 commit comments