From 944f849929d0b9206f6ccf606c22e0652b06ddce Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Wed, 15 Apr 2026 19:39:06 +0200 Subject: [PATCH] Revert "Fade out tvOS player controls on auto-hide" This reverts commit a65fbc44ff721a7cb73afd04c41bd44d58bcb034. --- Yattee/Views/Player/tvOS/TVPlayerView.swift | 72 +++++++++------------ 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/Yattee/Views/Player/tvOS/TVPlayerView.swift b/Yattee/Views/Player/tvOS/TVPlayerView.swift index ba5f2f6f..6dbea857 100644 --- a/Yattee/Views/Player/tvOS/TVPlayerView.swift +++ b/Yattee/Views/Player/tvOS/TVPlayerView.swift @@ -201,37 +201,34 @@ struct TVPlayerView: View { // Video layer videoLayer - // Controls overlay — always in tree, toggled via opacity so the fade-out - // isn't skipped by tvOS's focus engine forcibly tearing down a focused - // subview when the conditional flips to false. - TVPlayerControlsView( - playerState: playerState, - playerService: playerService, - focusedControl: $focusedControl, - onShowSettings: { showQualitySheet() }, - onShowQueue: { showQueueSheet() }, - onShowDetails: { showDetailsPanel(tab: .info) }, - onShowComments: { showDetailsPanel(tab: .comments) }, - onShowDebug: { showDebugOverlay() }, - onClose: { closeVideo() }, - onScrubbingChanged: { scrubbing in - isScrubbing = scrubbing - if scrubbing { - stopControlsTimer() - } else { - startControlsTimer() - } - }, - remoteSeekTime: scrubberRemoteSeekTime, - onRemoteSeek: { forward in - triggerScrubberRemoteSeek(forward: forward) - }, - cancelScrubTrigger: cancelScrubTrigger - ) - .opacity(shouldShowControls ? 1 : 0) - .allowsHitTesting(shouldShowControls) - .disabled(!shouldShowControls) - .animation(.easeInOut(duration: 0.25), value: shouldShowControls) + // Controls overlay + if controlsVisible && !isDetailsPanelVisible && !isDebugOverlayVisible { + TVPlayerControlsView( + playerState: playerState, + playerService: playerService, + focusedControl: $focusedControl, + onShowSettings: { showQualitySheet() }, + onShowQueue: { showQueueSheet() }, + onShowDetails: { showDetailsPanel(tab: .info) }, + onShowComments: { showDetailsPanel(tab: .comments) }, + onShowDebug: { showDebugOverlay() }, + onClose: { closeVideo() }, + onScrubbingChanged: { scrubbing in + isScrubbing = scrubbing + if scrubbing { + stopControlsTimer() + } else { + startControlsTimer() + } + }, + remoteSeekTime: scrubberRemoteSeekTime, + onRemoteSeek: { forward in + triggerScrubberRemoteSeek(forward: forward) + }, + cancelScrubTrigger: cancelScrubTrigger + ) + .transition(.opacity.animation(.easeInOut(duration: 0.25))) + } // Swipe-up details panel if isDetailsPanelVisible { @@ -385,13 +382,6 @@ struct TVPlayerView: View { } } - // MARK: - Derived State - - /// Whether the primary controls overlay should be visible right now. - private var shouldShowControls: Bool { - controlsVisible && !isDetailsPanelVisible && !isDebugOverlayVisible - } - // MARK: - Controls Timer private func startControlsTimer() { @@ -402,8 +392,9 @@ struct TVPlayerView: View { controlsHideTimer = Timer.scheduledTimer(withTimeInterval: 4.0, repeats: false) { _ in Task { @MainActor in - withAnimation(.easeOut(duration: 0.2)) { + withAnimation(.easeOut(duration: 0.3)) { controlsVisible = false + focusedControl = .background } } } @@ -671,8 +662,9 @@ struct TVPlayerView: View { private func hideControls() { stopControlsTimer() - withAnimation(.easeOut(duration: 0.2)) { + withAnimation(.easeOut(duration: 0.25)) { controlsVisible = false + focusedControl = .background } }