You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

tvOS SwiftUI:IMAManager广告结束后.onPlayPauseCommand失效求助

问题:tvOS SwiftUI中广告播放结束后.onPlayPauseCommand失效

在tvOS SwiftUI应用开发中,播放器首次加载时.onPlayPauseCommand可正常响应,但当IMAManager加载的广告播放结束后,该命令停止工作,其余命令(如.onMoveCommand)仍能正常运行。

核心原因分析

IMAManager在广告播放期间会接管系统媒体命令的处理逻辑,广告结束后未正确恢复SwiftUI视图的命令响应链路;同时AVPlayerViewController的焦点状态或内置命令处理可能与SwiftUI的.onPlayPauseCommand产生冲突,导致事件无法传递到视图修饰符。

解决方案

方案1:通过状态触发视图刷新,重新绑定命令响应

在广告结束时触发视图的状态更新,强制SwiftUI重新渲染并绑定.onPlayPauseCommand。

修改VerticalPlayerView,添加状态变量并监听广告结束事件:

struct VerticalPlayerView: View {
    @ObservedObject var player: SimplePlayer
    @Binding var showControls: Bool
    @State private var hideTimer: Timer?
    @FocusState private var playPauseFocused: Bool
    @Environment(\.scenePhase) private var scenePhase
    @Namespace private var playerFocusScope
    private let sliderLift: CGFloat = 120
    // 添加状态变量用于触发视图刷新
    @State private var refreshCommandTrigger = false

    var body: some View {
        // ... 原有视图代码 ...
    }
    .onAppear {
        resetHideTimer()
        // 监听广告状态变化
        player.onChangeAdStatus = { adCompleted in
            if adCompleted {
                // 广告结束时触发刷新
                refreshCommandTrigger.toggle()
            }
        }
    }
    // 通过状态变化触发视图刷新
    .onChange(of: refreshCommandTrigger) {}
#if os(tvOS)
    .onPlayPauseCommand {
        if !showControls {
            showControls = true
            playPauseFocused = true
        }
        player.togglePlayPause()
        resetHideTimer()
    }
    .onMoveCommand { _ in
        if !showControls {
            showControls = true
            playPauseFocused = true
        }
        resetHideTimer()
    }
#endif
}

方案2:禁用AVPlayerViewController的内置媒体命令处理

AVPlayerViewController默认会处理播放暂停等媒体命令,可能与SwiftUI的命令修饰符冲突,可禁用其内置控制或转发命令。

修改PlayerViewControllerView的makeUIViewController方法及添加协调器:

struct PlayerViewControllerView: UIViewControllerRepresentable {
    let player: AVPlayer
    var showsPlaybackControls: Bool = true
    var onAdEnvironmentReady: ((UIView, UIViewController) -> Void)?

    func makeUIViewController(context: Context) -> AVPlayerViewController {
        let vc = AVPlayerViewController()
        vc.player = player
        vc.showsPlaybackControls = showsPlaybackControls

        #if os(tvOS)
        vc.videoGravity = .resizeAspect
        vc.requiresLinearPlayback = false
        // 设置代理拦截媒体命令
        vc.delegate = context.coordinator
        #endif

        DispatchQueue.main.async {
            self.onAdEnvironmentReady?(vc.view, vc)
        }
        return vc
    }

    func updateUIViewController(_ vc: AVPlayerViewController, context: Context) {
        vc.player = player
        vc.showsPlaybackControls = showsPlaybackControls
    }

    // 添加协调器处理代理逻辑
    func makeCoordinator() -> Coordinator {
        Coordinator()
    }

    class Coordinator: NSObject, AVPlayerViewControllerDelegate {
        // 拦截播放暂停命令,交给SwiftUI处理
        func playerViewController(_ playerViewController: AVPlayerViewController, shouldHandlePlaybackControlOf type: AVPlayerViewControllerPlaybackControlType) -> Bool {
            if type == .playPause {
                return false
            }
            return true
        }
    }
}

方案3:手动注册UIKeyCommand(备选)

如果上述方案无效,可直接使用UIKit的UIKeyCommand手动注册播放暂停命令,绕开SwiftUI的命令修饰符限制。

创建播放器宿主视图:

struct PlayerHostView: UIViewControllerRepresentable {
    let player: SimplePlayer
    let onPlayPause: () -> Void

    func makeUIViewController(context: Context) -> UIViewController {
        let vc = UIViewController()
        // 注册播放暂停命令
        let playPauseCommand = UIKeyCommand(input: " ", modifierFlags: [], action: #selector(context.coordinator.handlePlayPause))
        vc.addKeyCommand(playPauseCommand)
        
        // 嵌入SwiftUI播放器视图
        let swiftUIPlayer = VerticalPlayerView(player: player, showControls: .constant(false))
        let hostingVC = UIHostingController(rootView: swiftUIPlayer)
        hostingVC.view.frame = vc.view.bounds
        vc.addChild(hostingVC)
        vc.view.addSubview(hostingVC.view)
        hostingVC.didMove(toParent: vc)
        
        context.coordinator.onPlayPause = onPlayPause
        return vc
    }

    func makeCoordinator() -> Coordinator {
        Coordinator()
    }

    class Coordinator: NSObject {
        var onPlayPause: (() -> Void)?

        @objc func handlePlayPause() {
            onPlayPause?()
        }
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}

内容的提问来源于stack exchange,提问作者steveSarsawa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 20:14:51