mirror of
https://github.com/yattee/yattee.git
synced 2026-11-06 11:57:18 +00:00
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:
@@ -8,16 +8,41 @@
|
||||
|
||||
#if os(iOS)
|
||||
|
||||
import Observation
|
||||
import UIKit
|
||||
|
||||
/// Provides supported interface orientations.
|
||||
/// Supports in-app orientation locking to the current orientation.
|
||||
@MainActor
|
||||
@Observable
|
||||
final class OrientationManager {
|
||||
static let shared = OrientationManager()
|
||||
|
||||
private init() {}
|
||||
|
||||
/// Whether `requestGeometryUpdate(.iOS(interfaceOrientations:))` is honored
|
||||
/// in the current windowing mode. False on the iPhone Duo interior display,
|
||||
/// where the system refuses programmatic rotation (`UISceneErrorDomain` 101)
|
||||
/// the same way it does for iPad multitasking. Set from the hinge state and
|
||||
/// cleared when a request is refused, so the player falls back to the
|
||||
/// iPad-style details toggle instead of doing nothing.
|
||||
var canRotateProgrammatically = true
|
||||
|
||||
/// `UISceneErrorDomain` code returned when the windowing mode forbids
|
||||
/// programmatic interface orientation changes.
|
||||
static let orientationChangeForbiddenErrorCode = 101
|
||||
|
||||
/// Records a refused rotation request; returns true when the error is the
|
||||
/// windowing-mode refusal (as opposed to a transient failure).
|
||||
@discardableResult
|
||||
func noteGeometryUpdateFailure(_ error: Error) -> Bool {
|
||||
let nsError = error as NSError
|
||||
guard nsError.domain == UISceneErrorDomain,
|
||||
nsError.code == Self.orientationChangeForbiddenErrorCode else { return false }
|
||||
canRotateProgrammatically = false
|
||||
return true
|
||||
}
|
||||
|
||||
/// The orientation to lock to when `isLocked` is true.
|
||||
/// Updated when orientation lock is enabled to capture current orientation.
|
||||
private var lockedOrientation: UIInterfaceOrientationMask?
|
||||
|
||||
Reference in New Issue
Block a user