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

Swift中为AVPlayerViewController添加视频下载按钮的实现方法

给AVPlayerViewController添加视频下载功能的实现方法

你已经用AVPlayerViewController实现了基础的视频播放,要加下载按钮其实分两步:添加自定义按钮到播放器界面,以及实现视频下载的核心逻辑。下面给你一步步拆解:

一、给AVPlayerViewController添加下载按钮

AVPlayerViewController自带播放控件栏,我们可以把下载按钮加到它的contentOverlayView(覆盖在播放器内容上方的视图),适配性更好。直接修改你现有代码即可:

let videoURL = URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")!
let player = AVPlayer(url: videoURL)
let playerViewController = AVPlayerViewController()
playerViewController.player = player

// 创建下载按钮
let downloadButton = UIButton(type: .system)
downloadButton.setTitle("下载", for: .normal)
downloadButton.addTarget(self, action: #selector(downloadTapped(_:)), for: .touchUpInside)
downloadButton.translatesAutoresizingMaskIntoConstraints = false

// 添加到播放器的overlay视图
playerViewController.contentOverlayView?.addSubview(downloadButton)

// 设置按钮约束(固定在右上角,和边缘留间距)
NSLayoutConstraint.activate([
    downloadButton.trailingAnchor.constraint(equalTo: playerViewController.view.trailingAnchor, constant: -16),
    downloadButton.bottomAnchor.constraint(equalTo: playerViewController.view.bottomAnchor, constant: -32)
])

// 弹出播放器
self.present(playerViewController, animated: true) {
    player.play()
}

二、实现视频下载核心逻辑

接下来编写downloadTapped方法,用URLSession处理后台下载,并把视频存到本地沙盒的Documents目录:

@objc func downloadTapped(_ sender: UIButton) {
    guard let videoURL = URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4") else { return }
    
    // 配置支持后台下载的URLSession
    let configuration = URLSessionConfiguration.background(withIdentifier: "com.yourApp.videoDownload")
    let session = URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue())
    
    // 创建并启动下载任务
    let downloadTask = session.downloadTask(with: videoURL)
    downloadTask.resume()
    
    // 临时修改按钮状态,避免重复点击
    sender.setTitle("下载中", for: .normal)
    sender.isUserInteractionEnabled = false
}

然后让你的ViewController遵守URLSessionDownloadDelegate协议,处理下载进度、完成和失败的回调:

extension YourViewController: URLSessionDownloadDelegate {
    // 实时更新下载进度
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
        print("下载进度:\(Int(progress * 100))%")
        
        // 更新UI必须回到主线程
        DispatchQueue.main.async {
            // 可选:给按钮添加进度提示
            // sender.setTitle("下载中:\(Int(progress*100))%", for: .normal)
        }
    }
    
    // 下载完成后处理文件存储
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        // 获取Documents目录路径
        guard let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
        // 生成目标文件路径(用服务器返回的文件名,默认用video.mp4)
        let destinationURL = documentsDir.appendingPathComponent(downloadTask.response?.suggestedFilename ?? "video.mp4")
        
        // 如果目标文件已存在,先删除
        if FileManager.default.fileExists(atPath: destinationURL.path) {
            try? FileManager.default.removeItem(at: destinationURL)
        }
        
        // 移动临时下载文件到目标路径
        do {
            try FileManager.default.moveItem(at: location, to: destinationURL)
            DispatchQueue.main.async {
                // 恢复按钮状态并提示用户
                self.downloadButton.setTitle("下载完成", for: .normal)
                self.downloadButton.isUserInteractionEnabled = true
                
                let alert = UIAlertController(title: "提示", message: "视频已保存到本地", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "确定", style: .default))
                self.present(alert, animated: true)
            }
        } catch {
            print("文件保存失败:\(error.localizedDescription)")
            DispatchQueue.main.async {
                self.downloadButton.setTitle("下载失败", for: .normal)
                self.downloadButton.isUserInteractionEnabled = true
            }
        }
    }
    
    // 下载失败的处理
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        if let error = error {
            print("下载出错:\(error.localizedDescription)")
            DispatchQueue.main.async {
                self.downloadButton.setTitle("重新下载", for: .normal)
                self.downloadButton.isUserInteractionEnabled = true
            }
        }
    }
}

额外优化建议

  • 避免重复下载:点击按钮前可以先检查本地是否已有该视频,对比文件名或URL的哈希值即可。
  • UI美化:可以把按钮换成带下载图标的样式,或者添加环形进度条提升用户体验。
  • 后台下载适配:如果APP退到后台后需要继续下载,要确保在AppDelegate或SceneDelegate中处理未完成的下载任务。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:56:24