On a half-open foldable the side column takes exactly the trailing half,
so the split lands on the hinge. Fully open, the column can now be
resized up to half the width. A re-presented player seeds the hinge
state from the last report so it doesn't animate into the split.
The in-app orientation lock pinned the player to the orientation it
opened in even where programmatic rotation is refused, so rotating the
half-open device did nothing. OrientationManager now ignores lock
requests and drops the current lock while canRotateProgrammatically is
false. The YATTEE_FORCE_TABLETOP override only applies on the interior
display size and flags the same state, matching a real .partiallyOpen
hinge instead of forcing the layout on the folded exterior display.
The interior display refuses programmatic rotation
(UISceneErrorDomain 101: "The current windowing mode does not allow for
programmatic changes to interface orientation"), so the rotate/fullscreen
button did nothing there and the failure was swallowed.
- OrientationManager tracks `canRotateProgrammatically` (from the hinge
state, cleared when a geometry request is refused) and is @Observable.
- The fullscreen button takes the iPad path when rotation is unavailable:
details toggle in portrait, floating panel toggle in landscape, icon
follows the panel state; orientation lock is hidden. The refused request
itself falls back to the details toggle. Accelerometer-driven rotation
requests are gated the same way.
- Corner layout gate is size-only on iOS (≥900×600 including safe areas,
so the status bar toggling with the layout can't oscillate it); the
layout-mode setting is exposed on foldables via a hinge presence check.
- Corner layout on iPhone always hides the status bar (the interior's
landscape bar is a ~57pt column) and pads by the live top inset only,
instead of the transient 82pt inset seen while unfolding.
- The player hosting controller nudges its safe area after size
transitions so fold/unfold doesn't leave SwiftUI with stale insets.
Observe the foldable hinge with `onHingeChange` (iOS 27.1): while it is
partially open and the portrait panel is hidden, the video is fitted to
the upper half of the screen and every control (title bar, center
controls, side sliders, bottom bar) moves to the lower half. Pinch to
fill is disabled in that state. `YATTEE_FORCE_TABLETOP=1` in the launch
environment forces the layout on simulators without a hinge control.
The exterior display has a vertical status bar with a trailing safe
area inset, which squeezed the fullscreen portrait player into the
content width. The video and controls layers now span the full window
width (shifted under the leading inset, like the existing vertical
handling) and the controls pad their edges by the sheet's actual
asymmetric horizontal insets instead of the symmetric max.
Requires the iOS 27.1 SDK (Xcode 27.1).