UIImageController로 촬영한 이미지를 메타데이터와 함께 저장하기

shintwl·2024년 7월 2일
0
post-thumbnail

촬영하기

let imagePickerController = UIImagePickerController()
imagePickerController.sourceType = .camera
imagePickerController.delegate = self
self.present(imagePickerController, animated: true)

Delegate 설정

extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        Task {
            do {
                try await viewModel.savePhoto(info)
            } catch {
                DispatchQueue.main.async {
                   print(error.localizedDescription)
                }
            }
        }
        // 카메라 창 닫기
        dismiss(animated: true)
    }
}

이미지 저장

UIImage Extension

UIImage 클래스는 메타데이터를 가지고 있지 않기 때문에
Data 형식으로 바꾼 후 메타데이터를 write 해준다

HEIC가 JPEG보다 압축 성능이 좋다고 해서 HEIC로 했다
https://support.apple.com/en-gb/116944

아이폰 7 이후로는 HEIC를 지원하기 때문에 24년 7월 기준으로는 문제 없을 것

fileprivate extension UIImage {
    func heif(with metadata: [String: Any]) -> Data? {
        guard let mutableData = CFDataCreateMutable(nil, 0),
              let destination = CGImageDestinationCreateWithData(mutableData, "public.heic" as CFString, 1, nil),
              let cgImage = cgImage else { return nil }
        
        CGImageDestinationAddImage(destination, cgImage, metadata as CFDictionary)
        CGImageDestinationFinalize(destination)
        return mutableData as Data
    }
}

갤러리에 이미지 저장

func savePhoto(_ info: [UIImagePickerController.InfoKey : Any]) async throws {
    guard let originalImage = info[.originalImage] as? UIImage else { throw SavePhotoError.noImage }
        
    let imageMetadata = info[.mediaMetadata] as? [String: Any]
        
    do {
        try await saveImageToPhotos(image: originalImage, metadata: imageMetadata)
    } catch {
        throw error
    }
}

private func saveImageToPhotos(image: UIImage, metadata: [String: Any]?) async throws {
    return try await withCheckedThrowingContinuation { continuation in
        PHPhotoLibrary.shared().performChanges({
            if let metadata = metadata,
               let imageInHEIFWithMetaData = image.heif(with: metadata) {
                   let request = PHAssetCreationRequest.forAsset()
                   request.addResource(with: .photo, data: imageInHEIFWithMetaData, options: nil)
            } else {
                   PHAssetCreationRequest.creationRequestForAsset(from: image)
            }
        }, completionHandler: { success, error in
            if success {
                continuation.resume()
            } else if let error = error {
                continuation.resume(throwing: error)
           }
        })
    }
}

참고 이미지

카메라갤러리

0개의 댓글