어찌저찌 살다보니 포스팅이 늦어졌네요ㅜㅜ
오늘은 사진 앨범 및 사진을 불러오는 방법을 알아보겠습니다.
iOS든 안드로이드든 앱에서 사진을 불러오려면 먼저 사용자가 권한을 허용해주어야 합니다.
import Photos
...
let requiredAccessLevel = PHAccessLevel.readWrite
let status = PHPhotoLibrary.authorizationStatus(for: requiredAccessLevel)
if status == .authorized || status == .limited {
//사진 앨범 및 사진 불러오기
viewModel.fetchAlbumList()
viewModel.fetchData()
}
else if status == .notDetermined {
//권한 요청하기
PHPhotoLibrary.requestAuthorization(for: PHAccessLevel.readWrite) { (status) in
if status == .authorized || status == .limited {
//사진 불러오기
viewModel.fetchAlbumList()
viewModel.fetchData()
} else {
DispatchQueue.main.async {
//TODO: 권한 없음 팝업 띄우기
}
}
}
}
else {
//TODO: 권한 없음 팝업 띄우기
}
권한 상태는 PHPhotoLibrary의 authorizationStatus 메소드를 이용하여 확인합니다. PHAuthorizationStatus.authorized는 "모든 사진에 대한 접근 허용"을, PHAuthorizationStatus.limited는 "선택한 사진에 대한 접근 허용"을 의미합니다. PHAuthorizationStatus.notDetermined는 아직 권한을 허용하거나 거부하지 않은 상태로, 이 상태일때만 사용자에게 팝업을 통한 권한 요청이 가능합니다. PHAuthorizationStatus.denied는 거부 상태입니다. limited나 denied 상태에서는 앱 내에서 권한 설정이 불가능하며, 설정 화면으로 넘겨주어 사용자가 직접 권한을 설정하도록 유도해야합니다.
//설정 화면으로 보내기
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else { return }
if UIApplication.shared.canOpenURL(settingsUrl) {
UIApplication.shared.open(settingsUrl)
}
이제 사진을 불러올 차례입니다.
안드로이드 개발 시에는 어떤지 잘 모르겠지만, iOS 개발을 할 때는 사진이나 앨범을 바로 불러오는 것이 아니라 PHAsset 또는 PHAssetCollection 이란 것을 불러와야 합니다.
PHAsset은 사용자가 가지고 있는 사진이나 동영상 등에 대한, PHAssetCollection은 앨범(사용자가 직접 만든 앨범, moment, 스마트 앨범)의 메타데이터 객체라고 이해하시면 될 것 같습니다.
저는 이번에 앨범 목록을 먼저 가져오고, 해당 앨범의 사진을 가져오도록 구현해보겠습니다.
let favoritesCollection = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumFavorites, options: nil)
let selfiesCollection = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumSelfPortraits, options: nil)
let userAlbumCollections = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumRegular, options: nil)
앨범 정보를 가져오기 위해서는 PHAssetCollection.fetchAssetCollections 메소드를 사용해야합니다. 이때, with와 subtype parameter를 통해 가져올 앨범을 특정할 수 있습니다. (ex: 스마트 앨범의 "즐겨찾기" 등) 이 메소드가 호출되면 앨범 정보가PHFetchResult<PHAssetCollection> 타입으로 리턴됩니다.
let options = PHFetchOptions()
options.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.image.rawValue)
options.sortDescriptors = [ NSSortDescriptor(key: "creationDate", ascending: false) ]
favoritesCollection.enumerateObjects { assetCollection, index, _ in
let assets = PHAsset.fetchAssets(in: assetCollection, options: options)
if assets.count > 0 {
self.albumList.append(AlbumInfo(collection: assetCollection, title: "즐겨찾는 항목", count: assets.count, assets: assets))
}
}
selfiesCollection.enumerateObjects { assetCollection, index, _ in
let assets = PHAsset.fetchAssets(in: assetCollection, options: options)
if assets.count > 0 {
self.albumList.append(AlbumInfo(collection: assetCollection, title: "셀피", count: assets.count, assets: assets))
}
}
userAlbumCollections.enumerateObjects { assetCollection, index, _ in
let assets = PHAsset.fetchAssets(in: assetCollection, options: options)
if assets.count > 0 {
self.albumList.append(AlbumInfo(collection: assetCollection, title: assetCollection.localizedTitle ?? "", count: assets.count, assets: assets))
}
}
PHFetchResult<PHAssetCollection> 은 여러개의 PHAssetCollection을 가지고 있다고 생각하시면 이해가 편합니다. NSArray와 같이 PHFetchResult의 enumerateObjects 메소드를 사용하면 각각의 PHAssetCollection을 사용할 수 있습니다.
해당 앨범의 사진 정보들을 가져오기 위해서는, PHAsset.fetchAssets 메소드를 사용합니다. 이 메소드는 PHFetchResult<PHAsset> 타입을 반환합니다. 저는 앨범 정보와 해당 앨범의 사진 정보들을 하나의 클래스로 사용할 수 있도록 구현했습니다.
class AlbumInfo: Equatable {
static func == (lhs: AlbumInfo, rhs: AlbumInfo) -> Bool {
lhs.assetCollection == rhs.assetCollection
}
var assetCollection: PHAssetCollection?
var title: String
var count: Int
var assets: PHFetchResult<PHAsset>!
init(collection: PHAssetCollection?, title: String, count: Int, assets: PHFetchResult<PHAsset>) {
self.assetCollection = collection
self.title = title
self.count = count
self.assets = assets
}
}
사진 정보들을 가져왔으니 이제 실제 사진을 불러오기만 하면 됩니다.
실제 사진은 PHImageManager / PHCachingImageManager 를 사용하여 불러올 수 있습니다.
let imageManager = PHCachingImageManager()
imageManager.allowsCachingHighQualityImages = true
self.albumList[selectedIndex].assets.enumerateObjects { asset, index, _ in
imageManager.requestImage(for: asset, targetSize: .init(), contentMode: isFullImage ? .default : .aspectFill, options: imageOptions) { image, _ in
guard let image = image else return
//TODO: 이미지 로드 완료 시 동작
}
}
requestImage 메소드를 사용하여 이미지를 로드합니다. 이 때, targetSize에 .init()을 사용하면 원본 사이즈 그대로, 특정 값을 지정한 CGSize를 사용하면 해당 사이즈에 맞춰서 이미지를 로드합니다.
'iOS > Swift' 카테고리의 다른 글
[SwiftUI] UIImagePickerController를 이용한 카메라 사용 (0) | 2022.04.05 |
---|