diff --git a/Yattee/Extensions/UIApplication+ForegroundScene.swift b/Yattee/Extensions/UIApplication+ForegroundScene.swift new file mode 100644 index 00000000..bc0672cf --- /dev/null +++ b/Yattee/Extensions/UIApplication+ForegroundScene.swift @@ -0,0 +1,30 @@ +// +// UIApplication+ForegroundScene.swift +// Yattee +// +// The window scene and window the user is looking at, for code that has no +// view of its own to ask. `connectedScenes.first` is not that: on the iPhone +// Duo it can be the other panel's scene, and it ignores which one is active. +// + +#if canImport(UIKit) +import UIKit + +extension UIApplication { + /// The foreground-active scene, else a foreground-inactive one (Control + /// Center or Notification Center open over the app), else any scene. + var foregroundWindowScene: UIWindowScene? { + let scenes = connectedScenes.compactMap { $0 as? UIWindowScene } + return scenes.first { $0.activationState == .foregroundActive } + ?? scenes.first { $0.activationState == .foregroundInactive } + ?? scenes.first + } + + /// The key window of `foregroundWindowScene` (the expanded player's window + /// while it is up), else its first window. + var foregroundWindow: UIWindow? { + let scene = foregroundWindowScene + return scene?.windows.first { $0.isKeyWindow } ?? scene?.windows.first + } +} +#endif diff --git a/Yattee/Services/Player/TVDisplayModeManager.swift b/Yattee/Services/Player/TVDisplayModeManager.swift index c3d8511c..206614cf 100644 --- a/Yattee/Services/Player/TVDisplayModeManager.swift +++ b/Yattee/Services/Player/TVDisplayModeManager.swift @@ -146,12 +146,7 @@ final class TVDisplayModeManager { } private func activeDisplayManager() -> AVDisplayManager? { - let scenes = UIApplication.shared.connectedScenes - .compactMap { $0 as? UIWindowScene } - let scene = scenes.first { $0.activationState == .foregroundActive } - ?? scenes.first - let window = scene?.windows.first { $0.isKeyWindow } ?? scene?.windows.first - return window?.avDisplayManager + UIApplication.shared.foregroundWindow?.avDisplayManager } private func makeFormatDescription(for dynamicRange: TVDisplayDynamicRange) -> CMVideoFormatDescription { diff --git a/Yattee/Views/Components/VideoSwipeActionsModifier.swift b/Yattee/Views/Components/VideoSwipeActionsModifier.swift index 2af22f3c..9cb0545d 100644 --- a/Yattee/Views/Components/VideoSwipeActionsModifier.swift +++ b/Yattee/Views/Components/VideoSwipeActionsModifier.swift @@ -186,9 +186,9 @@ struct VideoSwipeActionsModifier: ViewModifier { activityItems: [appEnvironment?.shareURL(for: video) ?? video.shareURL], applicationActivities: nil ) - if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, - let window = windowScene.windows.first, - let rootVC = window.rootViewController { + // The key window is the expanded player's while it is up, so the + // share sheet opens above it rather than behind it. + if let rootVC = UIApplication.shared.foregroundWindow?.rootViewController { // Find the top-most view controller var topVC = rootVC while let presented = topVC.presentedViewController { diff --git a/Yattee/Views/Player/ExpandedPlayerSheet+Layouts.swift b/Yattee/Views/Player/ExpandedPlayerSheet+Layouts.swift index a757843e..08a42c61 100644 --- a/Yattee/Views/Player/ExpandedPlayerSheet+Layouts.swift +++ b/Yattee/Views/Player/ExpandedPlayerSheet+Layouts.swift @@ -1767,9 +1767,7 @@ extension ExpandedPlayerSheet { Text(verbatim: "fullH: \(Int(fullHeight)) offset: \(Int(controlsOffset))") Text(verbatim: "pinned: \(isPanelPinned ? "Y" : "N") vis: \(isPanelVisible ? "Y" : "N") side: \(panelSide == .left ? "L" : "R")") #if os(iOS) - let orientation = UIApplication.shared.connectedScenes - .compactMap { $0 as? UIWindowScene } - .first?.interfaceOrientation + let orientation = UIApplication.shared.foregroundWindowScene?.interfaceOrientation Text(verbatim: "orient: \(orientation == .landscapeLeft ? "LL" : orientation == .landscapeRight ? "LR" : "P")") #endif } diff --git a/Yattee/Views/Player/ExpandedPlayerSheet+Orientation.swift b/Yattee/Views/Player/ExpandedPlayerSheet+Orientation.swift index 7ca635c2..c925d8f2 100644 --- a/Yattee/Views/Player/ExpandedPlayerSheet+Orientation.swift +++ b/Yattee/Views/Player/ExpandedPlayerSheet+Orientation.swift @@ -15,12 +15,7 @@ extension ExpandedPlayerSheet { /// Get safe area insets from the window. var windowSafeAreaInsets: UIEdgeInsets { - UIApplication.shared.connectedScenes - .compactMap { $0 as? UIWindowScene } - .first? - .windows - .first? - .safeAreaInsets ?? .zero + UIApplication.shared.foregroundWindow?.safeAreaInsets ?? .zero } // MARK: - Orientation Lock diff --git a/Yattee/Views/Player/WideScreenPlayerLayout.swift b/Yattee/Views/Player/WideScreenPlayerLayout.swift index 7de32af5..9a295b91 100644 --- a/Yattee/Views/Player/WideScreenPlayerLayout.swift +++ b/Yattee/Views/Player/WideScreenPlayerLayout.swift @@ -118,11 +118,7 @@ struct WideScreenPlayerLayout: View { /// requiring `.foregroundActive` would drop the safe area to zero while /// they are open and make the panel jump to the screen edge. private var foregroundWindow: UIWindow? { - let scenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene } - let scene = scenes.first { $0.activationState == .foregroundActive } - ?? scenes.first { $0.activationState == .foregroundInactive } - ?? scenes.first - return scene?.windows.first { $0.isKeyWindow } ?? scene?.windows.first + UIApplication.shared.foregroundWindow } #endif @@ -187,9 +183,7 @@ struct WideScreenPlayerLayout: View { // deployment target allows, and consider `traitCollection.verticalBarEdge` (iOS 27.1) // for the bar-edge question. Global scene lookup should be replaced by the hosting // window (e.g. via a UIViewRepresentable reading `view.window?.windowScene`). - let interfaceOrientation = UIApplication.shared.connectedScenes - .compactMap { $0 as? UIWindowScene } - .first? + let interfaceOrientation = UIApplication.shared.foregroundWindowScene? .interfaceOrientation ?? .unknown // landscapeLeft = home/gesture bar on left = Dynamic Island on RIGHT // landscapeRight = home/gesture bar on right = Dynamic Island on LEFT