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
相关产品推荐
相关产品推荐

