What happens
A native Switch draws correctly at rest — same size, shape and track colour as
UISwitch in Settings on the same OS (measured: 63.0x28.0pt, track
rgb(197,197,199), identical to the system control). The defect is in the
transition: for roughly half a second after a tap, the thumb is not drawn at
all.
Captured at 5fps on an iPhone 17 Pro simulator, toggling a switch from off to on (frame numbers are capture indices):
| frame | what is on screen |
|---|---|
| 033 | off: grey track, thumb left. Correct. |
| 034 | track green, thumb gone, a soft white blob detaching at the top-left |
| 035 | track fully green, thumb a faint outline, white notches cut out of the right edge |
| 036 | as above, thumb essentially invisible |
| 037 | thumb back on the right. Correct. |
Measured rather than eyeballed. Counting green pixels in the switch's rectangle, a settled on-switch is ~7,000. Frames 034-036 measure 11,000 and 13,000 — more green than a fully-on switch. That is only possible if the thumb is missing, because a correctly drawn thumb occupies ~5,000px of the track at every point along its travel. No frame of a correctly animating switch can exceed the settled value, so this is not an artifact of sampling mid-animation.
The same capture against the system control
The identical method, same simulator, same OS, same 5fps burst, toggling
Auto-Play Animated Images in Settings > Accessibility > Motion:
system : 0 … 0, 3, 7, 7, 7 … peak 7,411 (settled 7,139)
native : 0 … 0, 11, 13, 13, 7 … peak 13,000 (settled 7,000)The system control's green count rises to the settled value and stops. Its thumb is visible in every frame of the crossing. The native one overshoots the settled value by 1.8x for three frames, which is the thumb being absent.
That difference does not depend on how the control was driven, which matters because the two were driven differently: synthetic taps do not register on Settings' switches, so the system one was toggled by dragging while the app's was tapped. A drag shows the thumb under the finger and a tap animates it across, but in both the thumb covers part of the track throughout, so neither can exceed its settled green. One does.
What it is not
- Not app styling. The markup is
<Switch :value="..." @value-change="..." :accessibilityLabel="..." />with noclassand nostyle, soonTintColor,thumbTintColorandtrackColorare all nil and UIKit's own colours apply. - Not the app's state handling. It reproduces on a handler that sets the bound value synchronously and never touches the network, so there is no window where the script echoes a stale value back.
- Not
setOn(_:animated:)snapping.switchUserValuesholds the tapped value for 0.35s and the echo matches it, so the guard in the"Switch"case skipssetOnentirely. - Not clipping.
clipsToBoundsis only set foroverflow: hidden, scroll views, labels with a corner radius, image views and per-corner rounding. A bareSwitchis none of those.
What would help
A thumb that disappears mid-animation looks like the control being recreated
rather than updated — a freshly made UISwitch adopting the new value would
explain a missing thumb during the swap better than any property change. If
the reconciler replaces the node on a prop update instead of mutating it in
place, reusing it would fix this and remove a needless allocation per toggle.
I have not proven that, and it is a guess at the mechanism rather than a diagnosis. The reproduction and the measurements above are the solid part.