Skip to content

What SwiftUI Does

Jace edited this page Aug 30, 2026 · 1 revision

What SwiftUI does, and where Scritto differs

SwiftUI keeps only the ends of a value. Scritto keeps middles as well, and limits how far they travel. Everything here is measured, not read: this build's behaviour, not documented API. The reference is Text(...).contentTransition(.numericText()) on macOS 27 / Xcode 26.6, August 2026, driven through a real NSHostingView two ways: offscreen, sampling the text layer's frame every 8ms and capturing the layouts SwiftUI hands a custom TextRenderer (57 cases), and on screen, recording a 900×130 borderless window at 30fps, measuring ink travel by cross-correlating per-frame column profiles (36 cases across three sweeps). Raw matrix: research/apple-matrix.txt.

Rule: prefix and suffix, nothing else

It matches a common prefix and a common suffix. Nothing else.

shared content example result
prefix only abcdefabcXY prefix kept, no motion
suffix only abcdefXYdef suffix kept, slides 18px
both ends abcXXXdefabcYYYYYdef both kept, suffix slides 47px
middle only xxlightxxyylightyy not matched, everything dissolves
middle only, long zzzzdaylightzzzzqqqqdaylightqqqq not matched
middle only, a word say light nowthe light is not matched

There is no search for a run flush with neither end. the light you seek is within youyou are the daylight shares the word you, and SwiftUI never considers it: the line cross-dissolves where it stands, left to right.

Travel: as far as layout says

pair kept run travels
dlightlight 25px
daylightlight 69px
brightestlightlight 167px
extraordinarlightlight 224px
supercalifragilisticlightlight 337px
daytt, one glyph kept 10px

Every one of those is a smooth ramp, not a jump. Unlimited travel is safe for Apple because an end-anchored run never travels further than layout moves it. The distance is a consequence, not a search result.

Findings: the rest of the study

  • The text is anchored at its leading edge; the trailing edge does the moving. Across 14 recorded cases the left edge never moves.
  • .numericText() animates anything, not only digits. Words, CJK, Hebrew and emoji all transition. It is not gated on the string being a number.
  • .numericText(value:) behaves identically to .numericText() in every case.
  • .opacity and .identity never interpolate the advance. .interpolate, and no transition at all, snap the layout at the end.
  • Design (default, rounded, monospaced), size (14, 48, 96) and monospacedDigit() change the geometry but not the rule.
  • A custom TextRenderer is called only for the two endpoint layouts, so the morph runs on rendered text rather than by re-laying it out per frame.
  • Everything not kept cross-dissolves in place, blurring slightly, staggered left to right. Glyphs never fly to a new home.

Where Scritto agrees

Keeps are unbounded at either end. A kept prefix or suffix travels however far layout moves it. SwiftUI slides supercalifragilisticlightlight 337px in the recording above; the same pair measured here at 48px type slides 422px. Different renderers and capture rigs, same rule.

Anything can roll. Digits, words, emoji, CJK, Arabic-Indic digits. Nothing is gated on the value parsing as a number.

The unmatched middle dissolves in place rather than flying anywhere.

Where Scritto differs

Scritto searches for a run flush with neither end; SwiftUI does not. It wins real cases. 1 second2 seconds keeps second; Apple re-rolls the lot. xxlightxxyylightyy keeps light standing still, measured at 2px of drift.

A floating run needs a brake, because SwiftUI's rule is self-limiting and Scritto's is not. It may travel at most its own length plus two glyph slots. That keeps light still, lets 111,001 pay 74px for the separator that pushed its digits apart, and refuses a short word crossing a long value. the light you seek is within youyou are the daylight re-rolls, same as Apple, by a different route.

Direction is read from the numbers pairwise, not from the leading one alone. Every number in the old value is compared with its counterpart in the new one and the first pair that differs decides, so 2 minutes 12 seconds2 minutes 13 seconds reads as a rise even though the leading number never moves. Gaining or losing a number leaves nothing to compare position-for-position, so 59 seconds1 minute 0 seconds reads as a rise too, which is what the clock is doing.

Clone this wiki locally