What is a hitch versus a hang, how does ProMotion change the budget, and how do you measure and fix them?
Predict first
What is the per-frame budget at 60 Hz and at 120 Hz?
print(1000.0 / 60, 1000.0 / 120)
- 16.67 and 8.33
- 16.666666666666668 8.333333333333334
- 33.3 and 16.7
Mental model. A hitch is a train that arrives a minute late at one station. A hang is the whole line stopped. ProMotion shortens the timetable: trains now leave every 8 ms, so each stop gets half the time.
The display refreshes at a fixed rate and the system needs the next frame ready before each refresh. The budget is 1000 ms ÷ refresh rate: 16.7 ms at 60 Hz and 8.3 ms at 120 Hz (ProMotion iPhones and iPads, which adapt between about 10 and 120 Hz). Missing the deadline repeats the previous frame, a hitch, which the eye reads as stutter, especially in scrolling and animation. A hang is a longer unresponsiveness: the main thread is blocked so the app does not react to input; Apple’s tools count it from about 250 ms (shorter pauses of 100 to 250 ms are microhangs), and the system watchdog kills an app that stays blocked at launch or resume. So hitches are about rendering cadence, hangs about responsiveness, and fixing them uses different tools.
ProMotion makes things both better and harder. At 120 Hz the same work must fit in half the time, and animations driven by CADisplayLink or Core Animation can request a range with preferredFrameRateRange (CAFrameRateRange(minimum:maximum:preferred:)) so the system picks a rate that matches the content; on iPhone, custom Core Animation and CADisplayLink animations also need the CADisableMinimumFrameDurationOnPhone Info.plist key to run above 60 Hz. Measure with Instruments (the Animation Hitches template, Time Profiler for main-thread work, the Core Animation instrument), Xcode’s hitch and hang metrics in the organizer, and MetricKit (MXHangDiagnostic, scroll hitch rate) for field data. Typical fixes: move work off the main thread (decoding, parsing, formatting), cache expensive results, reduce offscreen passes and blending, flatten view hierarchies, avoid layout thrash (changing constraints repeatedly), prepare cells ahead with prefetching, and keep the main thread free for input. Always test on a real device, on the slowest supported hardware, in a Release build.
The budget is the refresh period: 1000 ms divided by the rate, so doubling the rate halves it.
Common traps
- Profiling in the simulator or a Debug build. Timings are not representative; use a Release build on a device.
- Assuming 120 Hz is automatic for your own animations. Custom display-link animations need a frame-rate range request and, on iPhone, the Info.plist key.
They might follow up with
- Which tool for a scrolling hitch? Instruments’ Animation Hitches, then Time Profiler for the main thread.
- Which for a frozen UI after launch? Hang diagnostics and the Time Profiler, looking for blocking calls on the main thread.