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

如何让Video Selection Controller中选中的视频可播放?

解决视频选择器无法播放视频的问题

我看了你的代码,现在的问题是你只获取了视频的首帧图片并显示,没有实现视频播放的逻辑。要达到类似Instagram的视频选择器效果,我们需要改造你的SelectVideoHeader,加入AVPlayer来处理视频播放,同时添加播放控制逻辑。下面是具体的解决方案:

1. 改造SelectVideoHeader,添加视频播放组件

首先我们需要给头部视图添加AVPlayer、AVPlayerLayer以及可选的播放按钮,用来控制视频播放:

import UIKit
import AVFoundation

class SelectVideoHeader: UICollectionReusableView {
    // 视频播放器相关
    private var videoPlayer: AVPlayer?
    private var videoPlayerLayer: AVPlayerLayer?
    
    // 首帧图片占位
    let videoSelectView: UIImageView = {
        let iv = UIImageView()
        iv.contentMode = .scaleAspectFill
        iv.clipsToBounds = true
        return iv
    }()
    
    // 播放按钮(可选,如果你需要用户点击播放)
    let playButton: UIButton = {
        let button = UIButton(type: .system)
        let playImage = UIImage(systemName: "play.circle.fill")?.withRenderingMode(.alwaysTemplate)
        button.setImage(playImage, for: .normal)
        button.tintColor = .white
        button.isHidden = true
        button.addTarget(self, action: #selector(togglePlay), for: .touchUpInside)
        button.setDimensions(width: 80, height: 80)
        return button
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupSubviews()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupSubviews() {
        addSubview(videoSelectView)
        videoSelectView.fillSuperview()
        addSubview(playButton)
        playButton.center(inView: self)
    }
    
    // 配置视频播放器,传入选中的PHAsset
    func configureVideo(with asset: PHAsset) {
        // 先移除旧的播放器层,避免重复添加
        videoPlayerLayer?.removeFromSuperlayer()
        
        // 请求视频资源(支持iCloud视频)
        let videoOptions = PHVideoRequestOptions()
        videoOptions.isNetworkAccessAllowed = true
        
        PHImageManager.default().requestAVAsset(forVideo: asset, options: videoOptions) { [weak self] (avAsset, _, _) in
            guard let self = self, let urlAsset = avAsset as? AVURLAsset else { return }
            
            self.videoPlayer = AVPlayer(url: urlAsset.url)
            self.videoPlayerLayer = AVPlayerLayer(player: self.videoPlayer)
            self.videoPlayerLayer?.frame = self.bounds
            self.videoPlayerLayer?.videoGravity = .resizeAspectFill
            self.layer.addSublayer(self.videoPlayerLayer!)
            
            // 两种模式可选:
            // 1. 自动播放(类似Instagram)
            // self.videoPlayer?.play()
            // self.playButton.isHidden = true
            
            // 2. 显示播放按钮,等待用户点击
            self.playButton.isHidden = false
            
            // 监听视频播放结束,重置播放状态
            NotificationCenter.default.addObserver(
                self,
                selector: #selector(self.handleVideoEnd),
                name: .AVPlayerItemDidPlayToEndTime,
                object: self.videoPlayer?.currentItem
            )
        }
    }
    
    // 切换播放/暂停状态
    @objc private func togglePlay() {
        guard let player = videoPlayer else { return }
        if player.timeControlStatus == .playing {
            player.pause()
            playButton.isHidden = false
        } else {
            player.play()
            playButton.isHidden = true
        }
    }
    
    // 视频播放结束后重置到开头
    @objc private func handleVideoEnd() {
        videoPlayer?.seek(to: .zero)
        playButton.isHidden = false
    }
    
    // 暂停视频(页面消失时调用)
    func pauseVideo() {
        videoPlayer?.pause()
        playButton.isHidden = false
    }
    
    // 移除通知监听,避免内存泄漏
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

2. 更新SelectVideoController的头部视图逻辑

修改viewForSupplementaryElementOfKind方法,现在我们不仅要设置首帧图片,还要配置视频播放器:

override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
    let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerIdentifier, for: indexPath) as! SelectVideoHeader
    self.header = header
    
    if let selectedImage = self.selectedImage {
        if let index = self.images.firstIndex(of: selectedImage) {
            let selectedAsset = self.assets[index]
            // 先加载首帧作为占位图
            let videoManager = PHImageManager.default()
            let targetSize = CGSize(width: 600, height: 600)
            videoManager.requestImage(for: selectedAsset, targetSize: targetSize, contentMode: .default, options: nil) { (image, _) in
                header.videoSelectView.image = image
            }
            // 配置视频播放
            header.configureVideo(with: selectedAsset)
        }
    }
    return header
}

3. 优化选中视频和页面生命周期的逻辑

在选中新视频时,先暂停当前视频;页面消失时也要暂停视频,避免后台播放:

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    // 先暂停当前播放的视频
    header?.pauseVideo()
    
    self.selectedImage = images[indexPath.row]
    self.collectionView?.reloadData()
    let headerIndexPath = IndexPath(item: 0, section: 0)
    collectionView.scrollToItem(at: headerIndexPath, at: .bottom, animated: true)
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    // 页面消失时暂停视频
    header?.pauseVideo()
}

关键说明

  • 两种播放模式:你可以选择自动播放(注释掉播放按钮显示代码,打开自动播放逻辑),或者显示播放按钮让用户手动触发播放
  • iCloud视频支持:设置PHVideoRequestOptions的isNetworkAccessAllowed为true,可以访问用户存储在iCloud上的视频
  • 内存管理:使用weak self避免循环引用,在deinit中移除通知监听,防止内存泄漏
  • 权限配置:确保你在Info.plist中添加了NSPhotoLibraryUsageDescription,申请相册访问权限

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:38:08