The KVO callback was calling updateLayout(), which has two problems for
content-driven updates:
1. The prevLayoutDetentIdentifier guard can block updates. When
updateDetents triggers animateChanges, the delegate callback
sheetPresentationControllerDidChangeSelectedDetentIdentifier fires
and updates selectedDetentIdentifier to the system-assigned custom
detent identifier. On the next KVO callback, prevLayoutDetentIdentifier
(.medium) no longer matches selectedDetentIdentifier, so the update
is silently dropped.
2. Re-reading frame.size.height from the view hierarchy instead of using
the observed bounds can return a stale value during rapid layout passes.
Fix: have the KVO callback call updateDetents directly with the observed
height, bypassing both the guard and the frame re-read. The guard in
updateLayout() is preserved for the manual/fallback code path.
https://claude.ai/code/session_01Rp7ef1h3fKh6fhStcjqLJ5
Replace the async JS bridge round-trip (onLayout → updateLayout()) with
native KVO observation on the content view's bounds. When React Native's
Yoga layout engine sets the frame, the KVO callback fires synchronously
on the same run loop iteration and calls updateLayout() directly. This
eliminates the timing issues that surfaced after enabling iOS 26 design.
https://claude.ai/code/session_01Rp7ef1h3fKh6fhStcjqLJ5