이번 주제는 PHImageManager, PHAssetChangeRequest, PHAssetCollectionChangeRequest, PHFetchResult 객체들에 대해서 알아보겠습니다.
이번 글을 읽기 전 PHAsset과 PHAssetCollection에 대해 아직 보지 못하셨다면, PhotoKit 알아보기(2) 글을 참고해 주세요.
먼저, PHAsset과 PHAssetCollection는 직접 수정할 수 없으며, 수정이 필요한 경우 각 PHAssetChangeRequest, PHAssetCollectionChangeRequest 객체를 이용해야 한다고, 이전 글을 통해 말씀드렸습니다. 하나씩 순서대로 알아보겠습니다.
PHAssetChangeRequest
일단 PHAsset를 편집하려면, PHPhotoLibrary의
func performChanges(
() -> Void,
completionHandler: ((Bool, Error?) -> Void)?
)
또는,
func performChangesAndWait(() -> Void)
메서드의 change block 내부에서 PHAssetChangeRequest객체를 통해서 편집할 수 있다.func performChanges
메서드는 변경 요청을 비동기적으로 한다. 때문에 completionHandler
가 있습니다.
func performChangesAndWait
메서드는 동기적으로 변경 요청을 한다는 차이가 있습니다.
생성하는 방법부터 알아보겠습니다.
생성
func create(image: UIImage) {
PHPhotoLibrary.shared().performChanges({
let request = PHAssetChangeRequest.creationRequestForAsset(from: image)
// PHAssetChangeRequest를 만들고 날짜, 위치 정보, 즐겨찾기등 설정할 수 있습니다.
}, completionHandler: nil)
}
⚠️
UIImage 객체는 원래 로드된 이미지 파일과 관련된 모든 메타데이터를 포함하지 않습니다(예: 지리적 위치, 카메라 모델 및 노출 매개 변수와 같은 Exif 태그). 이러한 메타데이터가 사진 라이브러리에 저장되도록 하려면, 이미지 I/O를 이용하여, 임시 파일을 만든 뒤, 파일 경로 URL을 통해 creationRequestForAssetFromImage(atFileURL:) 메서드를 이용하여 요청을 보낼 수 있습니다.
삭제
func delete(assets: [PHAsset]) {
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.deleteAssets(assets as NSArray)
}, completionHandler: nil)
}
삭제의 경우 deleteAssets(_:)
메서드를 사용합니다. 매개변수로 삭제할 PHAsset의 배열을 받게 되는데, NSFastEnumerator
를 상속받는 객체로 변경해 줘야 합니다. 때문에 NSArray
로 타입 캐스팅을 해주었습니다.
상태 수정
func toggleFavoriteForAsset(asset: PHAsset) {
PHPhotoLibrary.shared().performChanges({
let request = PHAssetChangeRequest(for: asset)
request.isFavorite = !asset.isFavorite
// 상태 변경
}, completionHandler: nil)
}
PHAsset을 통해 PHAssetChangeRequest객체의 인스턴스를 생성하고, 해당 인스턴스를 통해
- var creationDate: Date?
PHAsset이 원래 생성되었다고 주장하는 날짜와 시간입니다. - var location: CLLocation?
PHAsset과 함께 저장된 위치 정보입니다. - var isFavorite: Bool
PHAsset이 사용자의 즐겨찾기 중 하나로 표시되는지 여부를 나타내는 부울 값입니다. - var isHidden: Bool
PHAsset이 컬렉션에 숨겨져 있는지 여부를 나타내는 부울 값입니다.
PHAsset의 위 4가지 상태를 변경할 수 있습니다.
PHAssetCollectionChangeRequest또한 PHAssetChangeRequest 사용법이 비슷합니다. 프로퍼티와 메서드의 약간의 차이가 있어 직접 공식문서를 참고해 보세요.
이제, PHFetchResult에 대해 알아보겠습니다.
PHFetchResult
PHFetchResult는 asset또는 collection의 배열??, List?? 라고 생각하면, 이해가 쉬울 것 같습니다. object(at:)
메서드를 이용해서 최종적으로 asset을 가져오게 되는데 파라미터로 인덱스가 들어가게 됩니다. 배열이랑 비슷하지 않나요???
공식문서에 의하면, 가져오기에 대해선 thread-safe한 접근을 제공한다고 합니다.
만약, PHPhotoLibrary에 변경이 생겨 변경된 컨텐츠를 가져오려 한다면,
extension SFImagePicker: PHPhotoLibraryChangeObserver {
public func photoLibraryDidChange(_ changeInstance: PHChange) {
// PHFetchResult를 업데이트 하는 로직
}
}
위와 같이 PHPhotoLibraryChangeObserver
를 채택하고, photoLibraryDidChange(_ changeInstance: PHChange)
메서드를 구현한 뒤,
PHPhotoLibrary.shared().register(self)
위 처럼 등록하면, 변경에 대한 PHFetchResult를 처리할 수 있습니다.
PHFetchResult는 성능을 위해 가져온 콘텐츠를 캐시 하여, 가장 최근에 액세스 한 인덱스 주위에 객체 묶음을 유지합니다. 묶음 외부의 객체는 더 이상 캐시 되지 않기 때문에, 이러한 객체에 액세스하면 해당 객체를 다시 가져옵니다.
PHFetchResult에 대해 알아보았는데, asset을 가져오고, 그 과정에서 캐시까지 쉽지 않은 내용이 많은 것 같습니다.
마지막으로, 가져온 asset을 통해 이미지를 뽑아내는 작업에 필요한 PHImageManager에 대해 알아보겠습니다.
PHImageManager
PHImageManager의 인스턴스는 생성자 또는 default
(싱글톤 인스턴스)를 이용하여 생성할 수 있습니다.
PHImageManager는 이미지의 경우,
func requestImage(
for asset: PHAsset,
targetSize: CGSize,
contentMode: PHImageContentMode,
options: PHImageRequestOptions?,
resultHandler: @escaping (UIImage?, [AnyHashable : Any]?) -> Void
) -> PHImageRequestID
위 메서드를 통해 이미지를 가져올 수 있습니다. 메서드의 파라미터로는,
- asset
이미지 데이터를 로드할 자산입니다. - targetSize
반환할 이미지의 대상 크기입니다. - contentMode
이미지를 요청된 크기의 종횡비에 맞추는 방법에 대한 옵션입니다. - options
사진이 요청을 처리하고, 요청된 이미지의 형식을 지정하고, 진행률 또는 오류를 앱에 알리는 방법을 지정하는 옵션입니다. - resultHandler
이미지 로딩이 완료되면 호출되는 블록으로, 요청된 이미지 또는 요청 상태에 대한 정보를 제공합니다.
블록은 다음 매개변수를 사용합니다.- 결과
요청한 이미지입니다. - 정보
요청 상태에 대한 정보를 제공하는 사전입니다. 가능한 키와 값 은 이미지 결과 정보 키 를 참조하십시오 .
- 결과
이러한 메소드가 컨텐츠(이미지, 영상, 라이브 사진)별로 각각 구현되어 있습니다.
위 메서드는 비동기적으로 실행됩니다. 때문에 비동기적으로 결과를 처리하기 위해 resultHandler가 존재합니다. PHImageManager는 빠른 처리를 위해 고화질 이미지를 내보내기 전에 저화질 이미지가 먼저 보이고, 고화질 이미지가 처리되면, 고화질 이미지를 다시 한번 내보내 결과적으로 resultHandler가 두 번 이상 호출되게 됩니다. 만약 이 때문에 문제가 발생한다면, options의 isSynchronous를 ture로 설정한다면, 동기적으로 실행되고, resultHandler는 딱 한 번만 호출됩니다.
PHImageManager는 한번 가져온 asset의 이미지와 데이터를 캐시하고, 동일한 요청이 생기면 빠른 결과를 반환할 수 있으며, PHImageManager를 상속받은 자식 클래스인 PHCachingImageManager도 있습니다.
여기까지, PhotoKit에 대해서 알아보았습니다.
'iOS' 카테고리의 다른 글
[iOS] Clean Architecture 파헤치기 (0) | 2023.01.16 |
---|---|
[iOS] Coordinator Pattern (0) | 2023.01.09 |
[iOS]PhotoKit 알아보기(2) (0) | 2022.11.20 |
[iOS]PhotoKit 알아보기(1) (0) | 2022.11.20 |
[iOS] TextView Placeholder 구현하기 (0) | 2022.11.01 |