티스토리 뷰
안녕하세요 :) Zedd입니다.
벌써 추석이네요!! 즐거운 추석 보내세요 ㅎㅎ🌕🙏
오늘은 photoLibraryDidChange에 대해서 자세히 알아보려고 합니다.
iOS 14+ ) Select Photos 권한 작업 (1)
iOS 14+ ) Select Photos 권한 작업 (2)
글에서 photoLibraryDidChange를 한번 봤었는데요,
파라미터로 있는 changeInstance의 사용법(?)이 궁금해서 공부를 해보려고 합니다.
photoLibraryDidChange는 observer에게 사진 라이브러리에 뭔가 변경사항이 발생했음을 알리는 메소드에요.
저 changeInstance는 이름에서도 유추가 가능하실텐데, 뭐가 변했는지..변경사항을 나타내는 객체에요.
그래서 이 changeInstance를 사용하여 변경사항을 확인하고, 자세한 변경정보를 가져올 수 있습니다.
그럼 코드로 한번 봅시다.
github.com/Zedd0202/iOS14_Photos_limited_authorization
요 프로젝트를 보면서 공부해볼게요!
// MARK: - PHPhotoLibraryChangeObserver
func photoLibraryDidChange(_ changeInstance: PHChange) {
self.getCanAccessImages()
}
저번에는 photoLibraryDidChange에 이렇게만 되어있었어요.
대충 설명드리자면, 사진 더 선택할래? ➞ ㅇㅇ ➞ 사진 더 선택 ➞ photoLibraryDidChange 불림 ➞ getCanAccessImages에서 전체 asset을 가져온 뒤, 접근 할 수 있는 사진 거름 ➞ collectionView에 보여줌.
⚠️⚠️⚠️⚠️
photoLibraryDidChange가 사진 더 선택할래? 에서만 불리는게 아니라
처음 사진 권한 설정 ➞ limited권한 선택 ➞ PHPicker에서 사진 선택 ➞ Done누른 후에도 호출 됨
⚠️⚠️⚠️⚠️
여기서 문제는, getCanAccessImages의
func getCanAccessImages() {
self.canAccessImages = []
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
let fetchOptions = PHFetchOptions()
self.fetchResult = PHAsset.fetchAssets(with: fetchOptions) // 여기
...
}
이 부분입니다. 만약 사진을 3개를 더 선택했어요. 근데 전체 asset을 다시 가져와야하는 상황인거죠.
(자세한 코드는 iOS 14+ ) Select Photos 권한 작업 (2). 참고)
저는 그게 싫은겁니다!!!
photoLibraryDidChange의 changeInstance를 사용해 딱 내가 추가한(또는 제거한) asset만 가져올 수 있지않을까! 라는 생각입니다.
이해가셨나요 ㅠㅠ?....이 글이 뭔가 철저히 제 경험/생각 위주라..저만 알아볼 수 있는 글이 될까 걱정이네요.
# 변경사항 가져오기
func getCanAccessImages() {
self.canAccessImages = []
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
let fetchOptions = PHFetchOptions()
self.fetchResult = PHAsset.fetchAssets(with: fetchOptions) // 여기
...
}
자..fetchResult라는 변수 안에 fetchAsset을 한 결과를 할당해주고있습니다.
변경사항은 changeDetails(for:)이라는 메소드로 가져올 수 있는데요,
var fetchResult = PHFetchResult<PHAsset>()
// MARK: - PHPhotoLibraryChangeObserver
func photoLibraryDidChange(_ changeInstance: PHChange) {
let details = changeInstance.changeDetails(for: self.fetchResult)
..
}
이렇게 기존의 fetchResult를 넣어서 변경사항을 가져올 수 있습니다.
그래서 fetchResult가 할당되지 않은 상태에서 changeDetails를 호출하면 nil이 나오니 이 점 유의하세요!
상황을 하나 가정해보겠습니다.
1. 제가 기존에 limited권한을 주고, 사진을 3개 선택했었어요.
2. 사진 더 선택할래? alert이 떴을 때 ㅇㅋ하고 사진을 3개를 더 선택합니다.
3. photoLibraryDidChange가 불립니다.
이렇게 해서 저 details를 출력해보면,
var fetchResult = PHFetchResult<PHAsset>()
// MARK: - PHPhotoLibraryChangeObserver
func photoLibraryDidChange(_ changeInstance: PHChange) {
let details = changeInstance.changeDetails(for: self.fetchResult)
..
}
<PHFetchResultChangeDetails: 0x282ad2d60> before=<PHFetchResult: 0x2839e4210> count=3, after=<PHFetchResult: 0x2839d1a20> count=6, hasIncremental=0 deleted=(null), inserted=(null), changed=(null), hasMoves=0)
대충 이런 결과를 얻을 수 있습니다.
before, after가 있어서 변경사항을 한눈에 볼 수 있죠!
before에서는 선택된 asset이 3개였는데, after에는 6개가 된 걸 보니 3개를 더 선택했구나~ 라는 걸 알 수 있어요
// MARK: - PHPhotoLibraryChangeObserver
func photoLibraryDidChange(_ changeInstance: PHChange) {
guard let details = changeInstance.changeDetails(for: self.fetchResult) else { return }
details.fetchResultBeforeChanges // <PHFetchResult: 0x2806c0370> count=3
details.fetchResultAfterChanges // <PHFetchResult: 0x2806d8c60> count=6
}
이렇게 fetchResultBeforeChanges, fetchResultAfterChanges를 통해
before, after를 각각 가져올 수도 있습니다.
보면 count만 가져올 수 있는 것 같은데..그건 아닙니다!
guard let details = changeInstance.changeDetails(for: self.fetchResult) else { return }
details.fetchResultAfterChanges.enumerateObjects { (asset, _, _) in
//
}
이렇게 enumerateObjects를 통해 asset을 가져올 수도 있습니다.
주의하셔야 할 건, 내가 방금 3개를 "더 선택"했잖아요?
afeterChange에 그 추가된 3개만 들어있는게 아니라, 내가 이전에 선택한 3개도 "같이" 들어있는겁니다.
만약
코드가 눈에 들어오실진 모르겠는데;;;;
photoLibraryDidChange에서 getCanAccessImages()메소드를 호출하는게 아니라,
fetchResultAfterChanges에서 나온 asset들만 추가하는거죠.
저는 canAccessImages가 CollectionView의 DataSource인데, 이 친구를 비우지 않고, 그냥 바로 append해버리면
이전 3개 + 최종 선택된 6개 => 9개가 되어 사진이 중복되어 나옵니다.
그러니까
// MARK: - PHPhotoLibraryChangeObserver
func photoLibraryDidChange(_ changeInstance: PHChange) {
self.canAccessImages = []
....
}
뭐 대충 이런식으로 해주면 되겠죠??
# 사진 앨범 변경사항 체크
limited권한의 사진 선택/선택 해제와는 상관없이 photoLibraryDidChange에서는 사진 앨범의 변경사항도 알 수 있습니다.
details.changedIndexes, details.changedObjects
details.removedIndexes, details.removedObjects
details.insertedIndexes, details.insertedObjects
말 그대로 사진 앨범에서 뭔가 변하고, 지워지고, 추가되면 이 프로퍼티의 변화를 볼 수 있습니다.
limited권한시의 선택/선택 해제와는 상관없습니다. (하면 전부 nil, [] 나옴)
그럼 테스트해봅시다!
# 사진 추가 / 제거
앱을 실행시켜주고, 홈으로 가서 Photos앱을 실행시켜줍니다. 그리고 사진을 하나 추가해줬습니다.
그리고 다시 제 앱으로 돌아오겠습니다.
<PHFetchResultChangeDetails: 0x600002c51320> before=<PHFetchResult: 0x600003f70630> count=7, after=<PHFetchResult: 0x600003f64b00> count=8, hasIncremental=1 deleted=(null), inserted=<NSMutableIndexSet: 0x600000697540>[number of indexes: 1 (in 1 ranges), indexes: (7)], changed=(null), hasMoves=0
그럼 이렇게 detail이 찍히게 됩니다. count가 7에서 8로 늘어났고, inserted에 변화가 생겼네요!
사진이 추가가 됐으니까요..!?
insertedIndexes로 지금 추가된 asset들의 인덱스를 구할 수 있고, insertedObjects로 추가된 asset들을 가져올 수 있어요.
insertedObjects는 배열입니다! 현재 1개만 추가되었으니 배열에 1개만 들어가있어요.
제거는 굳이 안해볼건데, removedIndexes, removedObjects로 정보를 가져 올 수 있습니다.
# 사진 메타데이터 업데이트
changedIndexes, changedObjects는 메타데이터의 변화가 생긴 친구들을 가져올 수 있습니다.
예를들어,
Edit을 눌러 이미지를 편집한 뒤 다시 제 앱으로 들어오면, photoLibraryDidChange가 불리고
changedIndexes는 메타데이터가 바뀐 asset들의 인덱스, changedObjects는 메타데이터가 바뀐 asset들을 가져올 수 있습니다.
<PHAsset: 0x7fbaef2175f0> 4DFA2DC9-3EAC-46B2-88EB-2D22497065CE/L0/001 mediaType=1/0, sourceType=1, (300x299), creationDate=2020-09-17 05:57:50 +0000, location=0, hidden=0, favorite=0, adjusted=1
adjusted가 바뀌었죠? hidden, favorite같이 사진을 숨기거나, favorite에 추가해도 메타데이터가 변한거기때문에..불리게됩니다!
# hasIncrementalChanges
// MARK: - PHPhotoLibraryChangeObserver
func photoLibraryDidChange(_ changeInstance: PHChange) {
guard let details = changeInstance.changeDetails(for: self.fetchResult) else { return }
print(details.hasIncrementalChanges)
}
hasIncrementalChanges라는 프로퍼티도 있는데요, fetch결과의 변경사항을 "증분적으로" 나타낼 수 있는지 여부를 나타내는 Bool값이에요.
Q : 증분적으로..?? 이게 먼 소리.....ㅋㅋ
A :
hasIncrementalChanges가 false이면, fetch결과가 "원래 상태와 너무 다르기 때문에" incremental change information(증분 변경 정보)가 의미가 없습니다. 그러니까 기존 상태와 너무 달라지면 이 값이 false가 된다는 말 같아요. 이럴 때는 그냥 fetchResultAfterChanges를 사용하면 됩니다.
hasIncrementalChanges가 true면 위에서 언급한 insertedIndexes, removedIndexes, changedIndexes (or insertedObjects, removedObjects, changedObjects) 프로퍼티들을 사용하여 추가, 제거, 업데이트 된 객체들을 찾을 수 있는거구요.
이렇게 photoLibraryDidChange를 보는 건 마치겠습니다.
photoLibraryDidChange를 공부하면서
iOS 14+ ) Select Photos 권한 작업 (1)
iOS 14+ ) Select Photos 권한 작업 (2)
에 부족한 설명들이 몇개 있어서 ㅠㅠㅠ 급하게 추가했네요.
혹시 글에 틀린 설명이 있다면 댓글 부탁드립니다!
즐거운 추석 되세요~.~
'iOS' 카테고리의 다른 글
iOS ) UIVisualEffect (Blur, Vibrancy) (0) | 2020.10.09 |
---|---|
iOS 14+ ) [충격 실화] IDFA를 사용하려면 사용자 동의 받아야.. (7) | 2020.10.02 |
iOS ) removingPercentEncoding (0) | 2020.09.28 |
iOS ) HTML String을 WKWebView에 보여주기 / WKWebView높이를 contentSize로 (0) | 2020.09.26 |
ARKit ) ARSession / ARConfiguration (0) | 2020.09.23 |
- 회고
- Accessibility
- ios 13
- swift sort
- UIBezierPath
- fastlane
- 스위프트
- WKWebView
- swift delegate
- FLUTTER
- Combine
- github
- np-complete
- 피아노
- Xcode
- Git
- swift tutorial
- IOS
- Swift
- SwiftUI
- iOS delegate
- swift 공부
- swift3
- 제이슨 파싱
- WidgetKit
- WWDC
- actor
- np-hard
- swift array
- 스위프트 문법
- Total
- Today
- Yesterday