Repository navigation
Turn the gauge hands with Core Animation, not SwiftUI (#69) - #70
Merged
Merged
Conversation
The needle's travel time is the sampling interval, so it is always mid-sweep. As an animatable SwiftUI Shape that kept the whole panel re-evaluating on the main thread at the display's refresh rate, forever: 41.6% of a core at rest with ten gauges and thirteen charts. DialHand is a layer rotated by a CABasicAnimation, which the render server runs, so the main thread wakes once per sample and the needle still sweeps. An interrupted sweep continues from the angle the hand has reached, tracked rather than read back from the layer, since a 240-degree dial folds into plus or minus 180 degrees there. The hand is not hit-testable, so a tile's drag still starts on it. ImageRenderer cannot draw a layer, so Copy Image draws a dial with the still NeedleShape and PeakMarkShape instead. Measured with the saved layout, mean of six 5-second top readings after a 20-second warm-up: main (c887960) 41.6% hands not animated at all 8.6% chart plot-width reader removed 40.6% (not the cause) this change 6.8% Tests: DialHandTests pins the layer animation, the interrupted sweep, and the click pass-through; checks that a copied dial keeps its needle; compares where the live and still needles land at five readings; and fails on any SwiftUI .animation or withAnimation in MonitorUI outside an allowlist. That guard was checked by putting the old animation back: it failed.
Contributor
Author
|
Checked on screen by the operator, 2026-09-25: the needles sweep smoothly and look the same as before. --opus |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #69.
The cause
The needle's travel time is the sampling interval, so it is always mid-sweep. As an animatable SwiftUI
Shape, that kept the whole panel re-evaluating on the main thread at the display's refresh rate. A 10-secondsampleof a fresh 1.9.0 showed the main thread never idle, running display-cycle flushes and window layout back to back.The change
DialHand: the needle and peak mark areCAShapeLayers rotated by aCABasicAnimation. The render server runs the sweep, so the main thread wakes once per sample.ImageRenderercannot draw a layer, so a copied dial uses the stillNeedleShape/PeakMarkShape(liveHands: false).AGENTS.md: one Key Concepts bullet with the rule and the reason.Measurements
Your saved layout (10 gauges, 13 charts, 1 s sampling), release builds via
Scripts/make-app.sh. Each figure is the mean of six 5-secondtopreadings after a 20-second warm-up.main(c887960)GeometryReaderremovedTests
312 pass, and
swiftformat --lintis clean.ImageRenderer, which is why the still shapes exist..animation(orwithAnimationinMonitorUIoutside an allowlist (ReorderDrag's 0.12 s drop indicator). I checked it by putting the old animation back: it failed.Not verified
Scripts/make-app.sh && open .build/monitor.app).