Support the iPhone Duo interior display in the player

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.
This commit is contained in:
Arkadiusz Fal
2026-10-01 08:53:37 +02:00
parent 2d65bc756f
commit 2fd52fff27
11 changed files with 217 additions and 37 deletions

View File

@@ -134,8 +134,8 @@ struct ControlsSectionRenderer: View {
}
case .orientationLock:
// Only on iPhone (not iPad)
if !actions.isIPad, actions.onToggleOrientationLock != nil {
// Only where rotation is under the app's control (not iPad, not Duo interior)
if !actions.usesRotationFreeFullscreen, actions.onToggleOrientationLock != nil {
controlButton(
systemImage: actions.orientationLockIcon,
tint: actions.isOrientationLocked ? .red : .white