imagePickerController:保存视频以在应用的后续 运行 中再次播放

imagePickerController: Save video to play again on a subsequent run of the app

在我的应用中,用户使用内置 UIImagePickerController 从他们的库中选择一个视频。我可以检索 URL 并播放所选视频。这很好用。

但是,我还希望能够在用户退出并重新启动应用程序后稍后播放同一视频,而无需用户使用选择器重新选择它。我想不出办法做到这一点。我尝试同时使用 UIImagePickerControllerReferenceURLUIImagePickerControllerMediaURL。当我立即播放视频时,它们都有效。我尝试将这些 URL 保存在 userDefaults 中,并在下次 运行 应用程序时检索它们。我正确检索了 URL,但视频无法使用 AVPlayer 播放。我预计 UIImagePickerControllerMediaURL 会出现这种行为,因为这似乎是暂时的 URL,但我没想到 UIImagePickerControllerReferenceURL.

会出现这种情况

我也尝试过使用 PHAsset.fetchAssets(withALAssetURLs: <#T##[URL]#>, options: <#T##PHFetchOptions?#>) 但这没有 return 任何值。

如何保存对视频的引用或视频本身供以后使用?

试试这个方法,对我有用:

private func loadVideo(url: URL, saveUrl: Bool = false) {
    let player = AVPlayer(url: url)
    let playerController = AVPlayerViewController()
    playerController.player = player
    present(playerController, animated: true) {
        player.play()
    }

    guard saveUrl else { return }

    UserDefaults.standard.set(url, forKey: "keyVideoUrl")
    UserDefaults.standard.synchronize()
}

要按顺序获得权限,请在 Info.plist 文件中添加以下内容:

<key>NSPhotoLibraryUsageDescription</key>
<string>Message description in here</string>

现在请求许可并启动视频选择器:

PHPhotoLibrary.requestAuthorization { [weak self] (status) -> Void in
    guard status == .authorized else { return }

    let imagePicker = UIImagePickerController()
    imagePicker.delegate = self
    imagePicker.sourceType = .photoLibrary
    imagePicker.mediaTypes = [kUTTypeMovie as String]

    self?.present(imagePicker, animated: true, completion: nil)
}

终于使用这个didFinishPickingMediaWithInfo方法:

extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        self.dismiss(animated: true, completion: nil)

        guard let asset = info[UIImagePickerControllerPHAsset] as? PHAsset else { return }

        PHImageManager.default().requestAVAsset(forVideo: asset, options: PHVideoRequestOptions(),
                                                resultHandler: {(asset: AVAsset?, audioMix: AVAudioMix?, info: [AnyHashable : Any]?) -> Void in
            if let urlAsset = asset as? AVURLAsset {
                self.loadVideo(url: urlAsset.url, saveUrl: true)
            }
        })
    }

}

选择视频并播放后,您可以重新启动应用并尝试重播,如下所示:

if let url = UserDefaults.standard.url(forKey: "keyVideoUrl") {
    self.loadVideo(url: url)
}

这是完整的工作源代码ViewController.swift

感谢AamirR 和Max9xs 对我上述问题的评论,我选择实施的解决方案是将文件从TMP 目录移动到Documents 目录。然后播放 Documents 目录中的视频。这也避免了每次用户使用 imagePickerController 选择新视频时 TMP 目录都被视频填满的问题。

委托函数中的以下代码将移动文件:

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {

    let mediaType = info[UIImagePickerControllerMediaType] as! String
    if  mediaType == (kUTTypeMovie as String) {

        let TMPvideoURL = info[UIImagePickerControllerMediaURL] as! URL
        let TMPvideoPath = TMPvideoURL.path

        let manager = FileManager.default
        let documentsDirectory = manager.urls(for: .documentDirectory, in: .userDomainMask).last!
        let docVideoURL = documentsDirectory.appendingPathComponent("selectedLibraryVideo.mov")
        let docVideoPath = docVideoURL.path

        do {
            if manager.fileExists(atPath: docVideoPath) {
                try manager.removeItem(atPath: docVideoPath)
            }
            try manager.moveItem(atPath: TMPvideoPath, toPath: docVideoPath)
        }
        catch {
            print(error)
        }
    }

    photoLibraryPickerController?.dismiss(animated: false, completion: nil)

}

然后我可以通过引用 Documents 文件夹中的文件路径来播放视频。这很好用。

        let documentsDirectory = manager.urls(for: .documentDirectory, in: .userDomainMask).last!
        let docVideoURL = documentsDirectory.appendingPathComponent("selectedLibraryVideo.mov")
        let docVideoPath = docVideoURL.path