커뮤니티 뷰에서 사진 업로드 기능을 구현하면서,
UIImagePickerController가 iOS 18.5부터 Deprecated된다는 소식을 듣고
PHPickerViewController로 마이그레이션을 진행했다.
여러 장 선택, 보안성 강화, 안정적인 이미지 처리 등 장점이 있다고 한다.
선택한 순서대로 이미지 불러오기
PHPickerResult 배열은 항상 사용자가 선택한 순서와 같지 않을 수 있다.
따라서, 선택 순서를 보장하려면 다음 과정을 거친다:
- results.compactMap { $0.assetIdentifier }로 ID 배열을 추출한다.
- PHAsset.fetchAssets(withLocalIdentifiers: [추출한 ID 배열], options: nil)을 호출한다.
- 반환된 PHFetchResult를 순회해 추출한 ID배열과 PHAsset을 딕셔너리에 1:1로 담아준다.
- ID배열의 순서대로 PHAsset을 담은 배열을 생성한다
- 각 PHAsset을 이용해 PHImageManager로 이미지를 요청해 UIImage 배열을 만든다.
Rx로 순서 보장 이미지 불러오기
PHAsset → UIImage 변환은 비동기이므로 Single을 활용한다.
private func loadOrderedImages(from results: [PHPickerResult]) -> Single<[UIImage]> {
// 사진 고유 ID 추출
let ids = results.compactMap { $0.assetIdentifier }
guard !ids.isEmpty else { return .just([]) }
// ids 배열 순서대로 PHAsset을 담은 PHFetchResult 가져오기
let fetched = PHAsset.fetchAssets(withLocalIdentifiers: ids, options: nil)
// 사용자가 선택한 ID 순서대로 PHAsset 배열 복원
var dics: [String: PHAsset] = [:]
fetched.enumerateObjects { asset, _, _ in
dics[asset.localIdentifier] = asset
}
let assetsInOrder = ids.compactMap { dics[$0] }
// 각 PHAsset → 이미지 Single로 변환
let imageSingles: [Single<UIImage?>] = assetsInOrder.map { asset in
Single<UIImage?>.create { observer in
let option = PHImageRequestOptions()
option.isNetworkAccessAllowed = true
option.deliveryMode = .highQualityFormat
PHImageManager.default().requestImageDataAndOrientation(for: asset, options: option) { data, _, _, _ in
observer(.success(data.flatMap { UIImage(data: $0) }))
}
return Disposables.create()
}
}
return Single.zip(imageSingles)
.map { $0.compactMap { $0 } } // nil 제거
}
enumerateObjects
- PHFetchResult는 배열이 아니기 때문에 forEach 대신 enumerateObjects라는 메서드를 제공한다.
- 콜백 인자: (asset, index, stop) / 필요 시 stop.pointee = true로 조기 종료 가능.
PHPicker 설정 옵션 정리
// preselectedAssetIdentifiers을 사용하려면 반드시 .shared()로 설정해야 한다.
var config = PHPickerConfiguration(photoLibrary: .shared())
config.filter = .images
config.selectionLimit = 5
// UI에서 선택 순서를 숫자로 표시
config.selection = .ordered
// 가능한 한 원본 그대로 가져와 트랜스코딩을 방지.
config.preferredAssetRepresentationMode = .current
// 이미 선택된 항목들을 미리 체크해 보여줄 수 있다(iOS 15+).
// 위 메서드에서 사용한 ids 배열을 다른 곳에 저장해뒀다가 그대로 넣으면 된다.
config.preselectedAssetIdentifiers = selectedAssetIdentifiers
알게 된 점
- PHPickerResult는 순서가 보장되지 않으므로, assetIdentifier → PHAsset → UIImage 흐름으로 재정렬해야 한다.
- PHFetchResult는 배열이 아니므로 enumerateObjects(또는 object(at:))를 사용한다.
- preselectedAssetIdentifiers를 쓰면 selectionLimit는 총 허용 개수만 지정하면 되고, 남은 개수는 픽커가 자동 관리한다.
참고한 자료
[Swift/TIL #9] PHPickerViewController에 대하여
[TIL #9] 2023 / 04 / 03 ~ 2023 / 04 / 06 사진을 가져오려 하는데 iOS 14 이상부터는 UIImagePickerController 대신 PHPickerViewController를 사용하라고 하더라고요. 그래서 오늘은 PHPickerViewController에 대해서 알아보겠
ios-daniel-yang.tistory.com