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

@@ -22,7 +22,8 @@ struct PanelSettingsView: View {
@State private var commentsShowAvatars: Bool = true
@State private var commentsPillMode: CommentsPillMode = .pill
/// Corner layout exists on iPad and macOS only.
/// Corner layout exists on iPad and macOS, and on a foldable iPhone whose
/// interior display is large enough (see `hasHinge`).
static var supportsCornerLayout: Bool {
#if os(macOS)
return true
@@ -31,12 +32,17 @@ struct PanelSettingsView: View {
#endif
}
/// Foldable iPhone: the interior display qualifies for the corner layout.
@State private var hasHinge = false
private var showsLayoutSection: Bool { Self.supportsCornerLayout || hasHinge }
/// iPhone has no page bar: the list configures inline sections instead.
private var usesSections: Bool { PanelSettings.isSectionsOnlyDevice }
var body: some View {
List {
if Self.supportsCornerLayout {
if showsLayoutSection {
layoutSection
}
visibilitySection
@@ -66,6 +72,7 @@ struct PanelSettingsView: View {
.onChange(of: viewModel.activePreset?.id) { _, _ in
syncLocalState()
}
.observeHinge(hasHinge: $hasHinge)
}
// MARK: - Layout Section