Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit9a7b776

Browse files
chore: improve performance of indeterminate spinner (#184)
A user reported a constant 10% CPU usage whilst the Cursor svg failed to load. It turns out unnecessarily tying a looping animation to some state in SwiftUI is a bad idea. If you want to render a looping animation that's not tied to some state, you should use the CoreAnimation framework. In this case, we use a `CABasicAnimation`.We leave the determinate spinner unmodified, as it by definition must be tied to some SwiftUI state.Before:![before](https://github.com/user-attachments/assets/aadd00bd-d779-456d-9a2a-d72e24b085b1)After:![after](https://github.com/user-attachments/assets/ca788653-fbb2-469b-8bc8-2c0e5361945f)
1 parentf8a5ca5 commit9a7b776

File tree

1 file changed

+67
-25
lines changed

1 file changed

+67
-25
lines changed

‎Coder-Desktop/Coder-Desktop/Views/CircularProgressView.swift

Lines changed: 67 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,45 +8,35 @@ struct CircularProgressView: View {
88
varprimaryColor:Color=.secondary
99
varbackgroundColor:Color=.secondary.opacity(0.3)
1010

11-
@Stateprivatevarrotation=0.0
12-
@StateprivatevartrimAmount:CGFloat=0.15
13-
1411
varautoCompleteThreshold:Float?
1512
varautoCompleteDuration:TimeInterval?
1613

1714
varbody:someView{
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-
iflet value{
25-
// Determinate gauge
16+
iflet 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
privatefunc 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+
structIndeterminateSpinnerView:NSViewRepresentable{
75+
vardiameter:CGFloat
76+
varstrokeWidth:CGFloat
77+
varprimaryColor:NSColor
78+
varbackgroundColor:NSColor
79+
80+
func makeNSView(context _:Context)->NSView{
81+
letview=NSView(frame:NSRect(x:0, y:0, width: diameter, height: diameter))
82+
view.wantsLayer=true
83+
84+
guardlet viewLayer= view.layerelse{return view}
85+
86+
letfullPath=NSBezierPath(
87+
ovalIn:NSRect(x:0, y:0, width: diameter, height: diameter)
88+
).cgPath
89+
90+
letbackgroundLayer=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+
letforegroundLayer=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+
letrotationAnimation=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

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp