💐
이미지 선택 기능을 구현하기 위해 iOS 개발자들은 주로 UIImagePickerController를 사용해 왔다. 그러나 iOS 14 이후, Apple은 새로운 PHPickerViewController를 도입했다. 이 변화는 사용자의 프라이버시 보호를 강화하고 더 나은 사용자 경험을 제공하기 위한 목적에서 비롯되었다. 또한, YPImagePicker와 같은 타사 라이브러리를 사용하여 인스타그램과 같은 UI를 쉽게 구현할 수 있다.
이전 글에서는 iOS 14 이전에 사용하던 ImagePicker에 대해 알아보았다.
ImagePicker을 사용해서 이미지뷰에 띄우는 작업까지 수행했는데, 실제 앱에서는 이를 데이터베이스에 저장하는 것이 중요하다. 예를 들어 Realm 데이터베이스에 데이터를 저장하고, 앱 Sandbox내에 Document 폴더에 실제 이미지를 저장하는 작업이 필요하다.
이 작업은 따로 다른 글에 정리해두도록 하겠다.
아무튼, iOS 14버전 이후에는 PHPickerController를 사용하도록 되어있는데, 뭐가 달라졌나?
여러 장을 선택할 수 있다.
원래는 별도의 기능 구현이 필요했는데, PHPickerController는 이 기능을 기본적으로 제공한다.
https://developer.apple.com/documentation/photokit/phpickerviewcontroller
https://developer.apple.com/videos/play/wwdc2022/10023/
공식문서를 보면 여러 장을 선택할 수 있는 기능 외에도, 라이브 포토만 선택할 수 있다던지, 지연된 이미지 로딩 및 복구하는 UI라던지 하는 기능이 추가되었다.
사용해보자
// MARK: PHPickerController
var configuration = PHPickerConfiguration()
configuration.selectionLimit = 3
// 영상, 사진 분리
configuration.filter = .images
// configuration.filter = .any(of: [.videos, .images]) // 여러 종류 필터
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
present(picker, animated: true)
요렇게 PHPickerController를 선언해주고,
다른 기능들과 동일하게 delegate 프로토콜을 채택해준다.
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
let itemProvider = results.first?.itemProvider
// 옵셔널 해제 & 이미지 가져올 수 있는지
if let itemProvider = itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self) {
itemProvider.loadObject(ofClass: UIImage.self) { image, error in
self.photoImageView.image = image as? UIImage
}
}
picker.dismiss(animated: true)
}
자 이 상태로 돌려보면,
무시무시한 보라색 에러가 뜬다.
👿
UIImageView.image must be used from main thread only
하여튼 보라색 에러는 다 메인스레드에서 하라는 뜻인것같다.
이미지를 가져오는 건 비동기로 이루어지기 때문에, UI업데이트는 메인스레드에서 처리해줘야된다.
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
let itemProvider = results.first?.itemProvider
// 옵셔널 해제 & 이미지 가져올 수 있는지
if let itemProvider = itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self) {
itemProvider.loadObject(ofClass: UIImage.self) { image, error in
DispatchQueue.main.async {
self.photoImageView.image = image as? UIImage
}
}
}
picker.dismiss(animated: true)
}
요렇게 하면 동작을 잘 한다!
근데 이것도 왜 권한 없이 가능한가?
이것도 마찬가지로 Out of Process
갤러리에 있는 사진을 가져오는 것! 이미지 데이터만 가져오는 건 권한 없이 가능하다.
그래서 이미지 데이터 외에 다른 데이터를 가져오면,
override func viewDidLoad() {
super.viewDidLoad()
// MARK: PHAsset - 사진, 시간, 위치
PHAsset.fetchAssets(with: nil)
에러가 뜬다.
이제는 진짜 권한을 받아야 한다.
Privacy - Photo Library Usage Description
Privacy - Camera Usage Description
요렇게 두 개 허용해주면,
이렇게 허용 권한이 뜨고, 그리고 나머지 기능도 가능해진다.