mirror of
https://github.com/yattee/yattee.git
synced 2026-11-02 09:57:20 +00:00
Suspend the orientation lock while the Duo hinge is open
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.
This commit is contained in:
@@ -26,7 +26,17 @@ final class OrientationManager {
|
|||||||
/// the same way it does for iPad multitasking. Set from the hinge state and
|
/// 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
|
/// cleared when a request is refused, so the player falls back to the
|
||||||
/// iPad-style details toggle instead of doing nothing.
|
/// iPad-style details toggle instead of doing nothing.
|
||||||
var canRotateProgrammatically = true
|
/// While false, the in-app orientation lock is suspended as well: the
|
||||||
|
/// system decides the orientation there, so pinning `supportedOrientations`
|
||||||
|
/// only fights the user's physical rotation (Duo hinge open, simulator
|
||||||
|
/// forced tabletop).
|
||||||
|
var canRotateProgrammatically = true {
|
||||||
|
didSet {
|
||||||
|
if !canRotateProgrammatically && isLocked {
|
||||||
|
unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// `UISceneErrorDomain` code returned when the windowing mode forbids
|
/// `UISceneErrorDomain` code returned when the windowing mode forbids
|
||||||
/// programmatic interface orientation changes.
|
/// programmatic interface orientation changes.
|
||||||
@@ -52,7 +62,7 @@ final class OrientationManager {
|
|||||||
|
|
||||||
/// Lock orientation to the current orientation
|
/// Lock orientation to the current orientation
|
||||||
func lock() {
|
func lock() {
|
||||||
guard !isLocked else { return }
|
guard !isLocked, canRotateProgrammatically else { return }
|
||||||
lockedOrientation = currentInterfaceOrientationMask
|
lockedOrientation = currentInterfaceOrientationMask
|
||||||
isLocked = true
|
isLocked = true
|
||||||
notifyOrientationChange()
|
notifyOrientationChange()
|
||||||
@@ -60,6 +70,7 @@ final class OrientationManager {
|
|||||||
|
|
||||||
/// Lock to a specific orientation
|
/// Lock to a specific orientation
|
||||||
func lock(to orientation: UIInterfaceOrientationMask) {
|
func lock(to orientation: UIInterfaceOrientationMask) {
|
||||||
|
guard canRotateProgrammatically else { return }
|
||||||
lockedOrientation = orientation
|
lockedOrientation = orientation
|
||||||
isLocked = true
|
isLocked = true
|
||||||
notifyOrientationChange()
|
notifyOrientationChange()
|
||||||
@@ -67,6 +78,7 @@ final class OrientationManager {
|
|||||||
|
|
||||||
/// Re-lock to the current orientation (updates locked orientation even if already locked)
|
/// Re-lock to the current orientation (updates locked orientation even if already locked)
|
||||||
func lockToCurrentOrientation() {
|
func lockToCurrentOrientation() {
|
||||||
|
guard canRotateProgrammatically else { return }
|
||||||
lockedOrientation = currentInterfaceOrientationMask
|
lockedOrientation = currentInterfaceOrientationMask
|
||||||
isLocked = true
|
isLocked = true
|
||||||
notifyOrientationChange()
|
notifyOrientationChange()
|
||||||
|
|||||||
@@ -16,11 +16,24 @@ struct HingeLayoutObserver: ViewModifier {
|
|||||||
|
|
||||||
/// Debug override: launch with `YATTEE_FORCE_TABLETOP=1` to force the
|
/// Debug override: launch with `YATTEE_FORCE_TABLETOP=1` to force the
|
||||||
/// tabletop layout on simulators without a hinge control.
|
/// tabletop layout on simulators without a hinge control.
|
||||||
private static let forceTabletop = ProcessInfo.processInfo.environment["YATTEE_FORCE_TABLETOP"] == "1"
|
static let forceTabletop = ProcessInfo.processInfo.environment["YATTEE_FORCE_TABLETOP"] == "1"
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
func body(content: Content) -> some View {
|
||||||
if Self.forceTabletop {
|
if Self.forceTabletop {
|
||||||
content.onAppear { isTabletop = true }
|
// The override stands in for a half-open hinge, which only exists
|
||||||
|
// on the interior display: folded (exterior, 466×678) keeps the
|
||||||
|
// regular layout, like the real `.closed` state would.
|
||||||
|
content.onGeometryChange(for: Bool.self) { proxy in
|
||||||
|
min(proxy.size.width, proxy.size.height) >= ExpandedPlayerSheet.minHeightForCornerLayout
|
||||||
|
} action: { isInterior in
|
||||||
|
// Mirror the real hinge state: no programmatic rotation (and no
|
||||||
|
// orientation lock) while the hinge is open.
|
||||||
|
OrientationManager.shared.canRotateProgrammatically = !isInterior
|
||||||
|
guard isInterior != isTabletop else { return }
|
||||||
|
withAnimation(.easeInOut(duration: 0.3)) {
|
||||||
|
isTabletop = isInterior
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if #available(iOS 27.1, *) {
|
} else if #available(iOS 27.1, *) {
|
||||||
content.onHingeChange { _, context in
|
content.onHingeChange { _, context in
|
||||||
// `hinge` is nil on devices without a hinge and when the view
|
// `hinge` is nil on devices without a hinge and when the view
|
||||||
|
|||||||
Reference in New Issue
Block a user