iOS14で新しく登場したPHPickerViewControllerにおける動画の読み込み方法
PHPickerViewController
iOS14で新しくPHPickerViewControllerが追加されました。写真許可をユーザーからとる必要なく、また、OS標準の写真アプリとおなじUIで写真・動画選択ができるとても便利なものです。
Apple Documentation
動画の読み込み
PHPickerViewControllerの表示
func addButtonTapped(_ sender: Any) {
var configuration = PHPickerConfiguration()
configuration.filter = .videos
configuration.selectionLimit = 1
configuration.preferredAssetRepresentationMode = .current <- 動画を読み込む時のキモ
let phPicker = PHPickerViewController(configuration: configuration)
phPicker.delegate = self
present(phPicker, animated: true, completion: nil)
}
動画の読み込み方法
- NSItemProviderを使います
- resultsのなかにPHAssetのassetLocalIdentifierが入っていますが、それを用いて
PHAsset. fetchAssets(withLocalIdentifiers:options:)
とやってしまうと写真許可のアラートが表示されてしまいます。 - 写真許可がいらなく動画を読み込むためにはNSItemProviderを使います。
- ここで
NSItemProvider.loadFileRepresentation(forTypeIdentifier:completionHandler:)
を使います。 - 先ほどのPHPickerViewControllerの表示のところで PHPickerConfiguration.preferredAssetRepresentationModeを
.current
に設定しておくのが大事です。ここで.currentに設定しない場合、システムがどのバージョンの動画を読み込むかの判定に時間がかかるため、数秒のタイムラグが発生します。ここで.currentを指定しておけば、1秒以内に動画を再生させることができます。 (参考: https://developer.apple.com/forums/thread/652695?answerId=629922022#629922022)
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true, completion: nil)
guard let provider = results.first?.itemProvider else { return }
guard let typeIdentifier = provider.registeredTypeIdentifiers.first else { return }
if provider.hasItemConformingToTypeIdentifier(typeIdentifier) {
// not provider.loadInPlaceFileRepresentation(forTypeIdentifier: typeIdentifier) { [weak self] (url, success, error) in
// provider.loadFileRepresentation should be used
// PHPickerConfiguration.preferredAssetRepresentationMode should be set .current, otherwise loading takes too long
// https://developer.apple.com/forums/thread/652695?answerId=629922022#629922022
provider.loadFileRepresentation(forTypeIdentifier: typeIdentifier) { [weak self] (url, error) in
if let error = error { print("*** error: \(error)") }
if let url = url {
let asset = AVURLAsset(url: url)
// ...
}
}
}
}
追記)
provider.loadFileRepresentation(forTypeIdentifier:completionHandler:)
だと返されるURLがtemporaryのため、completion blockを抜けた後にurlにアクセスできなくなってしまいます。もっと長い時間URLを使いたい場合は以下のようにするといいです。
provider.loadItem(forTypeIdentifier: typeIdentifier, options: nil) { (url, error) in
if let url = url as? URL {
// do something with the url
}
}
サンプルレポジトリ
こちらに実際に動くプロジェクトをおいておきました。
https://github.com/justin999/PHPickerSampler