[iOS]PhotoKit 알아보기(최종)

2022. 11. 22. 22:19·iOS&Swift

이번 주제는 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&Swift' 카테고리의 다른 글

[Swift] Swift Concurrency와 GCD  (1) 2022.12.14
[Swift] Race Condition과 Thread Safe  (0) 2022.12.12
[iOS]PhotoKit 알아보기(2)  (0) 2022.11.20
[iOS]PhotoKit 알아보기(1)  (0) 2022.11.20
[iOS] TextView Placeholder 구현하기  (0) 2022.11.01
'iOS&Swift' 카테고리의 다른 글
  • [Swift] Swift Concurrency와 GCD
  • [Swift] Race Condition과 Thread Safe
  • [iOS]PhotoKit 알아보기(2)
  • [iOS]PhotoKit 알아보기(1)
Esiwon
Esiwon
iOS 개발 블로그
  • Esiwon
    시원한 코드 기록
    Esiwon
  • 전체
    오늘
    어제
    • 분류 전체보기 (70)
      • iOS&Swift (24)
      • git & github (1)
      • 코테 (41)
      • 네부캠 회고 (4)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 깃허브
  • 공지사항

  • 인기 글

  • 태그

    코테
    실버
    알고리즘
    완전탐색
    구현
    ios
    회고
    photoUI
    백준
    Swift
    다이나믹 프로그래밍
    노드
    dfs
    Combine
    그리디
    챌린지
    Race Condition
    동시성
    GCD
    PhotoKit
    이분탐색
    photos
    Property wrapper
    BFS
    네부캠
    재귀
    비동기
    탐색
    브루트포스 알고리즘
    코딩테스트
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Esiwon
[iOS]PhotoKit 알아보기(최종)
상단으로

티스토리툴바