How can I prevent Siri Remote Back button from dismissing a fullScreenCover on tvOS?

I'm developing a tvOS application using SwiftUI, and I have a custom video player presented using fullScreenCover.

.fullScreenCover(isPresented: $isPlayerPresented) {
    PlayerView()
}

I would like to implement the same kind of behavior commonly seen in video players:

  1. The player is presented full screen.
  2. When the playback controls are visible, pressing the Siri Remote Back button should hide the controls.
  3. The player should remain presented.
  4. Only when the controls are already hidden should pressing Back dismiss the player.

However, I am unable to intercept the Back button before the fullScreenCover is dismissed.

I have tried several approaches, including:

.onExitCommand {
    // Handle Back
}

and handling UIPress / .menu events through UIKit.

The problem is that when the Siri Remote Back button is pressed while the view is presented using fullScreenCover, the fullScreenCover is dismissed directly. My custom view does not appear to be able to prevent the dismissal.

I also tried placing a custom UIView / UITapGestureRecognizer inside the fullScreenCover to intercept the remote button event, but the presentation is still dismissed.

According to the SwiftUI documentation, onExitCommand responds to the tvOS exit command generated by the Menu button, but it does not appear to provide a way to prevent the system from dismissing a fullScreenCover in response to the Siri Remote Back button.

I also noticed that interactiveDismissDisabled(_:) does not appear to provide a solution for this particular tvOS behavior.

My questions

  1. Is the Siri Remote Back button expected to automatically dismiss a SwiftUI fullScreenCover on tvOS?
  2. Is there a supported API to intercept the Back button before the fullScreenCover is dismissed?
  3. Is there a way to tell SwiftUI that a fullScreenCover should not be dismissed by the Siri Remote Back button?
  4. If fullScreenCover is not intended for this use case, what is the recommended way to implement a custom full-screen video player that needs to control Back-button behavior?

The desired behavior is essentially:

Back button
    ↓
Are playback controls visible?
    ├── Yes → hide controls, keep player presented
    └── No  → dismiss player

I would appreciate any guidance on the intended tvOS API or recommended architecture for implementing this behavior.

Thanks!

Answered by DTS Engineer in 904065022

Hello @zacJI

Let me provide some more context here:

My questions

Is the Siri Remote Back button expected to automatically dismiss a SwiftUI fullScreenCover on tvOS?

Yes, fullScreenCover is backed by UIKit modal presentation, a UIViewController presented modally. The presentation controller handles the Menu and Back button and dismisses the view controller directly before SwiftUI has a chance to intercept it here.

Is there a supported API to intercept the Back button before the fullScreenCover is dismissed?

Not currently. The UIKIt layer catches the event before SwiftUI's responder chain runs;onExitCommand will not fire inside a fullScreenCover when Back is pressed. Please file an enhancement request for this feature using Feedback Assistant, include your sample project that runs and explain your use case. Reply with the FB number once filed. I will make sure it is routed correctly.

Is there a way to tell SwiftUI that a fullScreenCover should not be dismissed by the Siri Remote Back button?

No, and please include this in your feedback. interactiveDismissDisabled is available on tvOS but its documented scope is gesture-based dismissal, think dragging a sheet down or tapping outside a popover. The Siri Remote back button is outside of this scope, and i'm not aware of any modifier that intercepts it here.

If fullScreenCover is not intended for this use case, what is the recommended way to implement a custom full-screen video player that needs to control Back-button behavior?

Try an approach that presents the player in a conditional Zstack layer, which keeps it in SwiftUI's view hierarchy where onExitCommand can intercept it first.

ZStack {
     LibraryView()

     if isPlayerPresented {
         PlayerView(showControls: $showControls)
             .ignoresSafeArea()
             .zIndex(1)
             .onExitCommand {
                 if showControls {
                     showControls = false
                 } else {
                     isPlayerPresented = false
                 }
             }
     }
 }

One thing to be aware of, onExitCommand only fires when a descedant of that view holds tvOS focus. When controls are hidden, there's nothing focusable and pressing Back will background the app.

  @FocusState private var focusHolder: Bool
...

if !showControls {
    Button { showControls = true } label: {
        Color.clear
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .ignoresSafeArea()
    }
    .buttonStyle(.plain)
    .focused($focusHolder)
}

I hope this information is helpful to you.

 Travis

Hello @zacJI

Let me provide some more context here:

My questions

Is the Siri Remote Back button expected to automatically dismiss a SwiftUI fullScreenCover on tvOS?

Yes, fullScreenCover is backed by UIKit modal presentation, a UIViewController presented modally. The presentation controller handles the Menu and Back button and dismisses the view controller directly before SwiftUI has a chance to intercept it here.

Is there a supported API to intercept the Back button before the fullScreenCover is dismissed?

Not currently. The UIKIt layer catches the event before SwiftUI's responder chain runs;onExitCommand will not fire inside a fullScreenCover when Back is pressed. Please file an enhancement request for this feature using Feedback Assistant, include your sample project that runs and explain your use case. Reply with the FB number once filed. I will make sure it is routed correctly.

Is there a way to tell SwiftUI that a fullScreenCover should not be dismissed by the Siri Remote Back button?

No, and please include this in your feedback. interactiveDismissDisabled is available on tvOS but its documented scope is gesture-based dismissal, think dragging a sheet down or tapping outside a popover. The Siri Remote back button is outside of this scope, and i'm not aware of any modifier that intercepts it here.

If fullScreenCover is not intended for this use case, what is the recommended way to implement a custom full-screen video player that needs to control Back-button behavior?

Try an approach that presents the player in a conditional Zstack layer, which keeps it in SwiftUI's view hierarchy where onExitCommand can intercept it first.

ZStack {
     LibraryView()

     if isPlayerPresented {
         PlayerView(showControls: $showControls)
             .ignoresSafeArea()
             .zIndex(1)
             .onExitCommand {
                 if showControls {
                     showControls = false
                 } else {
                     isPlayerPresented = false
                 }
             }
     }
 }

One thing to be aware of, onExitCommand only fires when a descedant of that view holds tvOS focus. When controls are hidden, there's nothing focusable and pressing Back will background the app.

  @FocusState private var focusHolder: Bool
...

if !showControls {
    Button { showControls = true } label: {
        Color.clear
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .ignoresSafeArea()
    }
    .buttonStyle(.plain)
    .focused($focusHolder)
}

I hope this information is helpful to you.

 Travis

How can I prevent Siri Remote Back button from dismissing a fullScreenCover on tvOS?
 
 
Q