PHPickerViewController
是系統內建的一個照片選取工具,透過 PHPickerConfiguration
設定它,然後從它的 delegate function func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult])
取得用戶選取的結果。看起來非常簡單直覺,直到我踩了坑...
我的目標是要從回傳的 [PHPickerResult]
轉成 [PHAsset]
,首先要取得所有的 assetIdentifier
,接下來使用 PHAsset
的 fetchAssets(withLocalIdentifiers identifiers: [String], options: PHFetchOptions?) -> PHFetchResult<PHAsset>
得到 fetch results。
PHPickerResult
的 assetIdentifier
總是為 nil
?
從 PHPickerViewControllerDelegate
我們可以得到 results: [PHPickerResult]
,然後就能將它轉成 assetIdentifier
array
let identifiers = results.compactMap { $0.assetIdentifier }
但我發現 identifiers
總是空的...
研究了好久之後才發現問題出在 PHPickerConfiguration
。
當我們要初始化它的時候,不能直接使用 let config = PHPickerConfiguration()
,如果沒有提供 photo library 參數給它,assetIdentifier
就會總是為 nil
。因此正確的做法如下:
let configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
self.present(picker, animated: true, completion: nil)
PHFetchResult
跟 PHPickerResult
數量不一致?
好不容易取得正確的 identifiers,接下來就是要透過 identifiers 取得 PHAsset
了。我們可以這麼做:
let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers:identifiers, options:nil)
fetchResult.enumerateObjects() { asset, _, _ in
// Deal with the asset
}
但我發現 fetchResult.count
跟 identifiers.count
並非總是一致,可取得的 assets 可能會比 identifiers 少。明明我們確定 identifiers 都是正確且合法的,為什麼無法取得所有對應的 PHAsset 呢?
經過一番排查之後,我才發現這跟照片的存取權限有關:
如果使用者有限制 app 只能存取特定照片,即使
PHPickerViewController
可以讓使用者看到並選取任何照片,但實際可以取得的只有使用者允許的部分。
以上,就是我的踩坑之路。