티스토리 뷰

iOS

iOS 14+ ) Select Photos 권한 작업 (1)

Zedd0202 2020. 9. 20. 20:13
반응형

 

안녕하세요 :) Zedd입니다.

오늘은 iOS 14이상 && 사진앨범에 접근할 때 

사진 선택옵션이 추가되었습니다. 관련 대응을 기록하고자 합니다. 

현재 글과 다음 글 총 2편으로 이루어져있습니다. 최종 코드는 github에 올려놨습니다.

 

Zedd0202/iOS14_Photos_limited_authorization

Contribute to Zedd0202/iOS14_Photos_limited_authorization development by creating an account on GitHub.

github.com

 

예제 프로젝트 입니다.

우상단 Add버튼을 누른 뒤, 만약 모든 사진에 대해 허용을 누르면,

내 모든 사진을 가져와 collectionView에 뿌려주는 간단한 앱입니다.

하지만 iOS14에서는 Select Photos를 통해 내가 선택한 사진에만 접근 할 수 있도록 할 수 있습니다.

이렇게요. 같이해보겠습니다. 

먼저 권한 요청부터 해주겠습니다.

 

# 권한 요청. 

iOS 14 이전.

 

 

iOS 14 이후

 


참고로 

이 파라미터에는 PHAccessLevel타입이 들어갈 수 있습니다.

@available(iOS 14, iOS 8, *)
public enum PHAccessLevel : Int {

    
    @available(iOS 8, *)
    case addOnly = 1

    @available(iOS 8, *)
    case readWrite = 2
}

PHAccessLevel은 enum으로, addOnly와 readWrite가 있습닏니다. 

⚠️ 주의하셔야 할 점은, addOnly시에는 

이 alert이 뜨고 Select Photos를 눌러도!!!!!!!!! PHPicker가 안뜹니다. 

readWrite를 넘겨줘야 PHPicker가 뜨더라구요.

(이건 왜그런지 정확한 이유를 모르겠습니다. read권한이 없어서 인건가?)


아무튼 코드에서 보실 수 있다시피,

PHPhotoLibrary.requestAuthorization(for: .readWrite) { authorizationStatus in
    switch authorizationStatus {
    case .limited:
        print("limited authorization granted") // 선택한 사진에 대해서만 허용. 
    case .authorized:
        print("authorization granted") // 모든 권한 허용. 
    default:
        print("Unimplemented")
    }
}

limited case가 선택한 사진에 대해서만 권한을 허용하겠다~ 라는 뜻입니다.

 

# 권한 선택 후 작업

권한 요청이 비동기로 이루어지기 때문에..completion으로 작업해주겠습니다.

 

# 원하는 곳에서 호출 

 

앱 실행 후 Add 버튼을 누르면,

여기까지 할 수 있게 됩니다. 

주의하실 점은, 

권한 요청 alert에서 모든 사진에 대 허용을 누를 때 처럼, Select Photos를 호출하는 순간

switch authorizationStatus {
      case .limited:
          completion()
          print("limited authorization granted")
}

이게 호출되는게 아닙니다.

PHPicker가 나오고, 내가 Done을 누르면!!!

그제서야 "limited authorization granted"이 콘솔에 찍히게 됩니다.

 

# fetchAssets

핵심은, "내가 선택한 이미지에만 접근을 허용한다"입니다.

내가 Done을 누르면 모든 이미지가 아니라, 내가 선택한 이미지만 나와야하죠. 

(모두 나오게 하려고 해도 안나옴)

참고로 아래 코드 말고 다양한 방법이 있을 수 있습니다. 

 

핵심 코드인데요. 한줄씩 보겠습니다. 

 

1. 모든 asset호출. 

var fetchResult: PHFetchResult<PHAsset>?

self.fetchResult = PHAsset.fetchAssets(with: nil)

먼저 모든 asset을 불러와줍니다. 

근데 이게 디바이스 / 시뮬레이터 동작이 다릅니다.

 

디바이스

var fetchResult: PHFetchResult<PHAsset>?

self.fetchResult = PHAsset.fetchAssets(with: nil)

사진첩에 사진이 7개가 있음 -> 내가 사진을 3개 선택함 -> fetchResult의 count값 : 3

 

시뮬레이터 

var fetchResult: PHFetchResult<PHAsset>?

self.fetchResult = PHAsset.fetchAssets(with: nil)

 

사진첩에 사진이 7개가 있음 -> 내가 사진을 3개 선택함 -> fetchResult의 count값 : 7

그래서 디바이스로 돌리는 것을 추천드립니다. 

 

2. 위에서 불러온 asset들을 탐색하면서 image를 가져온다.

func getCanAccessImages() {
    self.canAccessImages = []
    let requestOptions = PHImageRequestOptions()
    requestOptions.isSynchronous = true

    let fetchOptions = PHFetchOptions()
    self.fetchResult = PHAsset.fetchAssets(with: fetchOptions)
  
    self.fetchResult.enumerateObjects { (asset, _, _) in
        PHImageManager().requestImage(for: asset, targetSize: self.thumbnailSize, contentMode: .aspectFill, options: requestOptions) { (image, info) in
            guard let image = image else { return }
            self.canAccessImages.append(image)
            DispatchQueue.main.async {
                self.collectionView.insertItems(at: [IndexPath(item: self.canAccessImages.count - 1, section: 0)])
            }
        }
    }
}

fetchResult를 돌면서 image를 요청합니다. targetSize와 contentMode는 알아서들 해주시고..

resultHandler로 image가 반환됩니다. 

collectionView에 표시해줘야하니...canAccessImages라는 배열에 image를 넣어줬습니다.

그리고 동시에 collectionView에 insert해줬습니다.

(reload 하구싶었는데, 타이밍이 애매해서 insert로 처리..)

 

2-1 두번째 방법 ⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️

func getCanAccessImages() {
    self.canAccessImages = []

    let fetchOptions = PHFetchOptions() // 안줘도됨 
    self.fetchResult = PHAsset.fetchAssets(with: fetchOptions)
    DispatchQueue.main.async {
        self.collectionView.reloadData()
    }
}    

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return self.fetchResult.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCollectionViewCell", for: indexPath) as! ImageCollectionViewCell
    let asset = self.fetchResult[indexPath.item]
    let requestOptions = PHImageRequestOptions()
    requestOptions.isSynchronous = true
    PHImageManager().requestImage(for: asset, targetSize: self.thumbnailSize, contentMode: .aspectFill, options: requestOptions) { (image, _) in
        cell.imageView.image = image
    }
    return cell
}

자...이러면 requestImage를 cell쪽에서 해도 됩니다;;;

2-1 두번째 방법은..위에서 말했듯이

내가 선택한 사진 개수와 상관없이 fetchResult.count == 내 모든 asset count

&&

내가 선택 안한 사진은 image를 못불러옴 

때문에

이런 현상이 나타납니다. 디바이스에서는 안그러니까 참고하세요. 이 글과 다음 글은 2-1방법이 아닌 

func getCanAccessImages() {
    self.canAccessImages = []
    let requestOptions = PHImageRequestOptions()
    requestOptions.isSynchronous = true

    let fetchOptions = PHFetchOptions()
    self.fetchResult = PHAsset.fetchAssets(with: fetchOptions)
  
    self.fetchResult.enumerateObjects { (asset, _, _) in
        PHImageManager().requestImage(for: asset, targetSize: self.thumbnailSize, contentMode: .aspectFill, options: requestOptions) { (image, info) in
            guard let image = image else { return }
            self.canAccessImages.append(image)
            DispatchQueue.main.async {
                self.collectionView.insertItems(at: [IndexPath(item: self.canAccessImages.count - 1, section: 0)])
            }
        }
    }
}

이 코드로 설명하겠습니다. (디바이스 / 시뮬레이터 둘 다 잘나오는 코드) 

이렇게 됩니다.

iOS 14+ ) Select Photos 권한 작업 (2) 읽으러가기

 

 

반응형