@@ -53,13 +53,9 @@ private struct RamRodProgressView: View {
5353
5454 var body : some View {
5555 ZStack {
56- Rectangle ( )
57- . fill ( Color ( white: 0.16 ) )
58- GeometryReader { proxy in
59- Color ( white: 0.82 )
60- . frame ( width: proxy. size. width * ( progress / 1.0 ) , alignment: . leading)
61- . animation ( . linear, value: progress)
62- }
56+ Rectangle ( ) . fill ( Color ( white: 0.16 ) )
57+
58+ ProgressBarShapeView ( progress: progress)
6359 }
6460 . clipShape ( shape)
6561 . overlay ( shape. stroke ( Color ( white: 0.25 ) , lineWidth: 1 ) )
@@ -71,6 +67,62 @@ private struct RamRodProgressView: View {
7167 }
7268}
7369
70+ /**
71+ You see, animating a white rectangle growing in width is a very expensive operation that SwiftUI
72+ is completely unable to do by itself without consuming an unhealthy amount of CPU,
73+ so this uses Core Animation instead to offload that expensive computation to the WindowServer/GPU.
74+ */
75+ private struct ProgressBarShapeView : NSViewRepresentable {
76+ typealias NSViewType = _Representable
77+
78+ var progress : Double = 0
79+
80+ func makeNSView( context: Context ) -> _Representable {
81+ _Representable ( frame: . zero)
82+ }
83+
84+ func updateNSView( _ nsView: _Representable , context: Context ) {
85+ nsView. progress = progress
86+ }
87+
88+ final class _Representable : NSView {
89+ private lazy var bar = CALayer ( )
90+
91+ @Invalidating ( . layout)
92+ var progress : Double = 0
93+
94+ override init ( frame frameRect: NSRect ) {
95+ super. init ( frame: frameRect)
96+
97+ platformLayer. addSublayer ( bar)
98+ bar. backgroundColor = . white
99+ bar. anchorPoint = CGPoint ( x: 0 , y: 0.5 )
100+ }
101+
102+ required init ? ( coder: NSCoder ) {
103+ fatalError ( )
104+ }
105+
106+ override func layout( ) {
107+ super. layout ( )
108+
109+ CATransaction . begin ( )
110+ CATransaction . setDisableActions ( true )
111+
112+ bar. position = CGPoint ( x: bounds. minX, y: bounds. midY)
113+ bar. frame. size. height = bounds. height
114+
115+ CATransaction . commit ( )
116+
117+ CATransaction . begin ( )
118+ CATransaction . setAnimationDuration ( 0.2 )
119+ CATransaction . setAnimationTimingFunction ( . init( name: . linear) )
120+ bar. frame. size. width = bounds. width * progress
121+ CATransaction . commit ( )
122+ }
123+ }
124+ }
125+
74126#if DEBUG
75127#Preview {
76128 VMInstallationWizard . preview ( step: . download)
0 commit comments