[회고] 신입 iOS 개발자가 되기까지 feat. 카카오 자세히보기

🍎 Apple/Swift

[Swift] PhotoKit으로 사진앱에서 사진 가져오기

inu 2022. 5. 22. 22:51
반응형

PhotoKit으로 사진앱 사진 가져오기

이번에 동아리과제에서 기본 사진앱에서 사진을 가져오고, 이를 CollectionView로 띄워주는 기능을 구현했습니다. 매번 ImagePicker를 사용하고 PhotoKit은 사용해보지 못했는데 이번기회에 사용해봐서 좋았어요.

이를 구현하기 위해 학습한 개념과 구현 과정을 정리합니다!

requestAuthorization

  • 애플리케이션에서 사용자의 Photo 앱에 접근하기 위해서는 권한이 필요합니다.
  • 먼저 plist에서 Privacy - Photo Library Usage Description 옵션을 입력합니다.
  • 해당 옵션에 입력된 String 값이 권한요청 Alert 창에 출력됩니다.

  • 그리고 앱 내부에서는 PHPhotoLibrary의 authorizationStatus 메서드 및 requestAuthorization 메서드를 사용합니다.
  • class func authorizationStatus(for: PHAccessLevel) : 현재 앱의 권한 상태를 반환합니다.
  • class func requestAuthorization(for: PHAccessLevel, handler: (PHAuthorizationStatus) -> Void) : Photo 앱에 접근하는 것에 대한 권한을 요청합니다.
  • 권한 상태는 총 5가지가 존재합니다.

권한 요청하기 예시 코드

    private func getPermissionIfNecessary(completion: @escaping (Bool) -> (Void)) {
        switch PHPhotoLibrary.authorizationStatus() {
        case .notDetermined:
            PHPhotoLibrary.requestAuthorization(for: .readWrite) { status in
                if status == .authorized {
                    completion(true)
                }
            }
        case .authorized:
            completion(true)
        default:
            completion(false)
        }
    }
  • 저는 권한이 있을 경우 completionHandler에 Boolean값을 전달해 작업을 수행하도록 구성했습니다.

PHAsset, PHAssetCollectoin, PHCollectionList

  • PhotoKit에서 Photo에 접근하여 데이터를 가져올 때는 PHAsset, PHAssetCollectoin, PHCollectionList 3가지 모델을 활용하게 됩니다.
  • PHAsset은 이미지 및 비디오, 라이브포토를 나타내는 모델입니다.
  • PHAssetCollectoin은 앨범을 나타내는 모델입니다.
  • PHCollectionList는 앨범을 그룹화한 모델입니다.

  • 각 모델들은 데이터를 가져올 수 있는 fetch 메서드를 보유합니다.
  • 이를 통해 PHFetchResult(사진 앱으로부터 가져온 데이터의 결과를 나타내는 객체)를 받아올 수 있습니다.

PHFetchOptions

  • PHFetchOptions는 Asset 혹은 Collection 객체를 가져올 때 이들에 대한 필터링 및 정렬을 정의할 수 있는 객체입니다.

  • 주로 NSPredicate 타입인 predicate를 사용하여 필터링을 정의하고, NSSortDescriptor 타입인 sortDescriptors를 사용하여 정렬을 정의합니다.

  • 각 데이터타입별로 사용할 수 있는 key값이 있기 때문에 이를 활용하여 필터링 혹은 정렬을 수행할 수 있습니다.
        let allPhotosOptions = PHFetchOptions()
        allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
  • sortDescriptors 활용 예시입니다. 저는 PHAsset의 creationDate key값을 기준으로 정렬을 수행했습니다.

PHFetchResult

  • PHFetchResult는 각 모델을 기반으로 받아온 데이터를 나타내는 객체입니다.

  • Generic 형식으로 작성되어 있기때문에 PHAsset, PHAssetCollectoin, PHCollectionList의 값을 PHFetchResult에 담아 가져올 수 있습니다. 내부에는 PHAsset, PHAssetCollectoin, PHCollectionList 데이터가 모여있습니다.
  • contains(), count(), object(at:) 등의 메서드를 사용해 내부에 존재하는 데이터를 탐색할 수 있습니다.

  • 내부 데이터를 하나씩 순회하면서 작업할 수 있는 enumerateObjects 메서드도 제공합니다.
  • 저는 이 메서드를 활용해서 이미지 에셋을 확인하고 가져왔습니다.

PHImageManager

  • PHAsset에는 UIKit에서 바로 활용할 수 있는 형태의 이미지를 직접적으로 가지고 있지 않기때문에 이를 추출하는 과정이 필요합니다.
  • 이럴 때 사용하는 것이 PHImageManager입니다.
  • PHImageManager는 Asset의 이미지 및 제공 데이터를 캐싱하는 특징이 있기 때문에 추후 같은 대상으로 이미지를 추출할 때 더 빠른 처리가 가능합니다.

  • 일반적으로 공유 PHImageManager 인스턴스를 default() 타입 메서드로 호출하여 사용합니다.
  • PHImageManager는 이미지뿐 아니라 비디오, 라이브포토에 대한 추출도 제공하지만 이번엔 이미지 추출에 대해서만 알아보겠습니다. 다른 데이터도 작업과정은 유사합니다.

  • requestImage 메서드 중 위의 메서드를 활용하여 UIImage를 가져올 수 있습니다.

PHImageRequestOptions

  • Imgae Manager 객체가 요청한 Image에 대한 표현에 영향을 미치는 옵션 객체입니다.
  • 이미지 처리의 동기적 처리를 설정하는 isSynchronous, 이미지 퀄리티 및 우선순위를 설정하는 deliveryMode 등 다양한 옵션을 제공합니다.
  • 그 외 자세한 옵션에 대해서는 공식문서를 참고해주세요.
  • isSynchronous는 false가 기본값이기 때문에 비동기적으로 처리하고 싶다면 false 옵션을 주어야합니다. 저는 기본 옵션에서 해당값만을 바꾸어 사용했습니다.

모든 사진 가져오기 예시 코드

    private func getImageFromPhotoApp() {
        let allPhotosOptions = PHFetchOptions()
        allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]

        let allPhotos = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: allPhotosOptions)
        allPhotos.enumerateObjects { (asset, count, stop) in
            let targetSize = CGSize(width: UIScreen.main.bounds.width, height: CGFloat(asset.pixelHeight) * UIScreen.main.bounds.width/CGFloat(asset.pixelWidth))
            let imageManager = PHImageManager.default()
            let optinos = PHImageRequestOptions()
            optinos.isSynchronous = true
            imageManager.requestImage(for: asset, targetSize: targetSize, contentMode: PHImageContentMode.aspectFit, options: optinos, resultHandler: { [weak self] (image, info) in
                if let image = image {
                    self?.photoArray.append(image)
                }
            })
        }
    }
  • 지금까지 학습한 내용을 바탕으로 PhotoApp에서 모든 사진을 가져오는 함수를 작성했습니다.
  • 비동기적으로 처리된 이미지는 photoArray라는 배열에 저장됩니다.
  • 해당 배열을 기반으로 CollectionView를 구성했습니다.
반응형

'🍎 Apple > Swift' 카테고리의 다른 글

[Swift] autoreleasepool  (2) 2022.05.27
[Swift] UNUserNotificationCenter 살펴보기  (0) 2022.05.26
[Swift] URLSession Cahce Policy  (1) 2022.05.18
[Swift] URL Loading System  (0) 2022.05.12
[Swift] CodingKeys / Custom CodingKeys  (0) 2022.05.12