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

如何监听AVPlayer的控制栏可见性变化事件?

检测AVPlayer控制栏可见状态并适配旧机型导航需求

问题说明

视频播放页面需全屏展示,新款带滑动手势导航的iPhone可通过手势退出页面,但iPhone 6s/7等旧机型无此功能,因此需要在AVPlayer控制栏可见时显示导航栏返回按钮,核心需求是检测AVPlayer控制栏的可见状态。

解决方案

由于SwiftUI原生VideoPlayer未提供控制栏可见状态的公开API,我们可以通过自定义AVPlayerViewController子类结合UIViewRepresentable来实现状态监听,具体代码如下:

1. 自定义播放器控制器子类

import SwiftUI
import AVKit

class CustomPlayerViewController: AVPlayerViewController {
    // 控制栏状态变化回调
    var controlsVisibilityChanged: ((Bool) -> Void)?
    
    private var autoHideTimer: Timer?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        showsPlaybackControls = true
        
        // 添加点击手势监听屏幕点击事件
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(toggleControls))
        tapGesture.cancelsTouchesInView = false
        view.addGestureRecognizer(tapGesture)
    }
    
    @objc private func toggleControls() {
        let isVisible = playbackControlsView?.alpha == 1.0
        controlsVisibilityChanged?(isVisible)
        
        // 模拟系统自动隐藏逻辑:显示控制栏后3秒无操作则隐藏
        autoHideTimer?.invalidate()
        if isVisible {
            autoHideTimer = Timer.scheduledTimer(withTimeInterval: 3.0, repeats: false) { [weak self] _ in
                self?.controlsVisibilityChanged?(false)
            }
        }
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        autoHideTimer?.invalidate()
    }
}

2. 封装为SwiftUI可用的View

struct PlayerContainerView: UIViewRepresentable {
    let videoURL: URL
    var onControlsVisibilityChange: ((Bool) -> Void)?
    
    func makeUIView(context: Context) -> UIView {
        let containerView = UIView(frame: .zero)
        let player = AVPlayer(url: videoURL)
        let playerVC = CustomPlayerViewController()
        
        playerVC.player = player
        playerVC.controlsVisibilityChanged = onControlsVisibilityChange
        playerVC.view.frame = containerView.bounds
        playerVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        
        containerView.addSubview(playerVC.view)
        context.coordinator.playerVC = playerVC
        player.play()
        
        return containerView
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {}
    
    func makeCoordinator() -> Coordinator {
        Coordinator()
    }
    
    class Coordinator {
        var playerVC: CustomPlayerViewController?
        
        deinit {
            playerVC?.player?.pause()
        }
    }
}

3. 改造原播放页面

struct VideoPlayerView: View {
    let videoURL: URL
    
    @State private var controlsAreVisible = true
    @State private var pageTitle = ""
    
    var body: some View {
        PlayerContainerView(videoURL: videoURL) { isVisible in
            DispatchQueue.main.async {
                controlsAreVisible = isVisible
            }
        }
        .edgesIgnoringSafeArea(.all)
        .navigationBarBackButtonHidden(!controlsAreVisible)
        .navigationBarHidden(!controlsAreVisible)
        .navigationBarTitle(pageTitle, displayMode: .inline)
        .statusBar(hidden: !controlsAreVisible)
        .onChange(of: controlsAreVisible) { isVisible in
            pageTitle = isVisible ? videoURL.lastPathComponent : ""
        }
    }
}

方案说明

  • 通过自定义AVPlayerViewController,添加点击手势监听用户操作,同步控制栏可见状态到SwiftUI的状态变量。
  • 用定时器模拟系统控制栏的自动隐藏逻辑,确保状态同步的准确性。
  • 根据控制栏状态动态切换导航栏返回按钮、导航栏和状态栏的显示,适配旧机型的退出需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 06:33:23