티스토리 뷰

iOS

iOS ) PhotoKit (2) - 미디어 가져오기

Zedd0202 2018. 11. 9. 18:13
반응형


글 날라가서 진짜 너무 화나서 안씁니다.

아 진짜!@!@!!!!!!!!!!!!!

아 ㅠ

진짜...진짜 후...찐짜 ㅠ

그래 해보자 그래


그래.....ㅎㅎㅎㅎㅎㅎ

뭐 날라갈수도 있는거 아니겠습니까

긍정적으로 생각합시다


읽기전에 PhotoKit (1)을 읽고오시면 도움이 됩니다. 


PhotoKit (2)



자, 그럼 우리가 뭘 하고 싶을까요, PhotoKit으로!!!!!!

사진을 가져오고 싶겠죠!!!!!!!!!!!!!!!!

가장 기본적인 사진 가져오는 걸 해봅시다.


사진을 가져오는데, 그걸 collectionView에 뿌려주자! 

일단 프로젝트를 만들어줍니다. 그리고 collectionView 넣어주고 기본적인 세팅은 알아서.



import Photos


를 해주고, 

자, 생각을 해봅시다.

이제 뭘 해야할까요? 

일단 사진(PHAsset)을 "가져와야"겠죠?

이 가지고 오는 작업을 fetch라고 하는데요.


그 전에!!!!!!!!!!!!!!!!!!!!!!!!!!!!

그 전에 여러분........빼먹은게 있지 않으신가요...

사진..사진을 가져오려면...뭐부터 해야할까요..

fetch를 하는 것도 좋지만..그 전에.......

사용자의 사진 앨범에 접근하기 위한 명시적인 권한이 필요합니다.

그쵸!?!?

info.plist에 가서



저렇게 재수없ㄱ게 말하면 안되구요

왜 써야하는지 이유를 간단하게 적는 것이 좋습니다.



ㅋ-ㅋ

이 alert는 사진앱에 접근하는 코드가 실행되면 뜨게 됩니다. 

그 중 하나가 fetch메소드죠. 


PHAsset에 fetch메소드는 여러가지가 있습니다.



이만큼;;


저흰 아직 왕보초니까 가장 만만한거 하나 해봅시다.



바로 이것...

이 메소드를 사용해서 asset들을 가져와봅시다.



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


이렇게요. option은 Optional이니..일단은 nil로 줘봅시다.


이렇게 되면..allPhotos는 무슨 타입이어야 할까요? 

fetchAsset의 리턴타입에 맞아야겠죠?

PHFetchResult<PHAsset>타입어야 합니다.


var allPhotos: PHFetchResult<PHAsset>?


이렇게요. 


import Photos


class ViewController: UIViewController {


    @IBOutlet weak var collectionView: UICollectionView!

    var allPhotos: PHFetchResult<PHAsset>?


    override func viewDidLoad() {

        super.viewDidLoad()

        self.collectionView.delegate = self

        self.collectionView.dataSource = self


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

        self.collectionView.reloadData()

}


자, 여기까지의 코드에요.


그럼 allPhotos에는 지금 내 사진앱에 있는 모든 사진들이 들어가있습니다.

하지만 기본적으로 fetch Result 결과에는 iTunes를 통해 디바이스에 동기화되거나 iCloud공유 앨범에 저장된 사진은 포함되지 않아요.

이건 나중에 option을 공부하면서...


자. 그럼 각각의 collectionView cell에 내가 가져온 사진을 하나하나 넣어줘야겠죠?



let asset = self.allPhotos?.object(at: indexPath.item)


or


let asset = self.allPhotos?[indexPath.item]

 

일단 그럼 cellForItemAt delegate메소드에서 각 cell에 해당 indexPath.item에 위치한 PHAsset을 넘겨줘야합니다.


PhotoKit(1)에서도 말했다 시피, PHFetchResult는 NSArray와 똑같은 convention으로 다루면 되기 때문에..

애플 예제 프로젝트에서는 object를 사용하여 꺼내줬지만, 저는 그냥 간편하게 subscript로...ㅎ


그럼 지금, cell은 PHAsset을 받은거죠?

asset에서 image를 꺼내서 cell에 있는 imageView에 뿌려주면 되는거겠죠?


근데 PHAsset이 이미지, 비디오, 라이브 이미지 이렇게[ 표현할 수 있는데,

일단 이미지만 생각하자면, PHAsset이 이미지를 들고 있는게 아닙니;다!!!!!

PhotoKit(1)에서도 말했다시피 PHAsset은 오직 메타데이터만 가지고 있습니다. 

(Assets contain only metadata. )





PHImageManager를 사용하여 이미지를 요청해야합니다.

야야 내가 asset줄테니까 이미지좀 조바;ㅣ;;

이런느낌;;


이럴수가 저번시간에 PHImageManager의 정의를 안봤군요

ㅋ_ㅋ;;;

아 여기서 PHImageManager의 정의 보는 건 상당히 제 미의 기준에 안맞는 글이 되는데

PhotoKit - PHImageManager/PHCachingImageManager

그래서 따로 공부 하핫 

위 글을 참고해주세요. 

아무튼 cell에서는 PHAsset을 받아 image manager를 사용해서 이미지를 요청하면 됩니다.


여담이지만...애플 예제에서는 cellForItemAt에서 


 // Request an image for the asset from the PHCachingImageManager.

        cell.representedAssetIdentifier = asset.localIdentifier

        imageManager.requestImage(for: asset, targetSize: thumbnailSize, contentMode: .aspectFill, options: nil, resultHandler: { image, _ in

            // The cell may have been recycled by the time this handler gets called;

            // set the cell's thumbnail image only if it's still showing the same asset.

            if cell.representedAssetIdentifier == asset.localIdentifier {

                cell.thumbnailImage = image

            }

        })

        return cell


이미지를 요청해서 cell의 프로퍼티에 이미지를 넣어주는 그런건데..

일단 저는 이게 맘에 안들어서..

아니 제가 갓-애플의 예제를 뭐라하는 건 아니지만, 상당히 개인적인 취향이라고 해야ㅕ할까요

아무튼 그래서..저는 

따로 localImageManager라느 ㄴ것을 만들었었는데, 

그래서 cell안에서 localImageManager한테 asset을 줘서 

completion으로 받은 이미지를 딱 set해주게 했었어요.

이러고 글을 날렸죠


근데 지금 와서 보니까..그냥 viewController쪽에서 localImageManager호출해줘서

 image를 아예 cell에 넘기는게 낫겠네요.

왜 저렇게 했었지?!

아니 뭐 코드에 정답은 없지만...제 코드 취향이라고 생각해주세요 :)



아무튼 여러분은 여러분의 개인 취향대로 requestImage를 하시면 됩니다.


 func requestIamge(with asset: PHAsset?, thumbnailSize: CGSize,  completion: @escaping (UIImage?) -> Void) {

        guard let asset = asset else {

            completion(nil)

            return

        }

        self.representedAssetIdentifier = asset.localIdentifier

        self.imageManager.requestImage(for: asset, targetSize: thumbnailSize, contentMode: .aspectFill, options: nil, resultHandler: { image, _ in

            // UIKit may have recycled this cell by the handler's activation time.

            // Set the cell's thumbnail image only if it's still showing the same asset.

            if self.representedAssetIdentifier == asset.localIdentifier {

               completion(image)

            }

        })

    }


아무튼 중요한건 imageManager의 requestImage메소드입니다.

말그대로 이미지를 "요청"하는 메소드죠. 


이 메소드를 호출하면 resultHandler에 UIImage가 담겨온답니다. 

참 쉽죠,,,,,,

하핫


주의 : PHAsset은 뭐라그랬죠? PHAsset 하나의 이미지, 비디오, 또는 라이브사진을 나타냅니다. 

그러니까, 지금 앱을 돌려보시면(아래에 깃헙 링크 있음)

정말 많은..저같은 경우에는 엄청 많은데, 카메라 롤에 있는 걸 "전부" 들고옵니다.

그러니까 이미지만 있는것이 아니라 비디오도 있고, 물론 이미지도 있고, GIF나..모든 asset들을 전부!!! request한거죠. 

엥 근데 우리 지금 request"image"한건데...음 여기서 image는 우리가 생각하는 그 image(비디오가 아닌)가 아니라 그냥 다 가져오는데, 거기서 thumnail(?) 이미지라고 생각하시면 될 것 같아요. 비디오 같은 경우에도 재생하기 전에 보여지는 thumnail이 있잖아요? 그걸 가져오면 된다고 보면 됩니다. 




이 메소드에 대해서 조금 알아야 할 사항이 있으니 같이 한 번 봅시다.

이 메소드는 사진 앱에서 asset이미지를 로드하거나 생성하는데요, 그런 다음, resultHandler블록을 호출하여 요청된 이미지를 제공합니다. 

요청을 보다 신속하게 처리하기위해, 이미지는 이미 캐시되어있거나, 더 효율적으로 생성 될 수 있기 때문에 target size보다 약간 큰 이미지를 제공 할 수 있습니다.

지정한 옵션과 asset의 현재 상태에서 따라, Photos는 네트워크에서 asset데이터를 다운로드 할 수도 있습니다.

(엥 네트워크를 쓴다는 말인가?!?!..........???, Photos may download asset data from the network.)

기본적으로 이 메소드는 비동기적으로 실행됩니다. 백그라운드 스레드에서 호출하는 경우, options파라미터의 isSynchronous프로퍼티를 true로 변경하여, 요청한 이미지가 준비되거나 오류가 발생 할 때 까지 calling thread(호출 쓰레드)를 차단 할 수도 있습니다.


비동기 요청의 경우, Photos는 resultHandler블록을 두번 이상 호출 할 수 있습니다.(ㅇㅎ..)

Photos는 먼저 블록을 호출하여, 고품질 이미지를 준비하는 동안, 일시적으로 표시하기에 적합한 저품질 이미지를 제공합니다.(오...)

(품질이 낮은 이미지 데이터를 즉시 사용 할 수 있는 경우, 메소드가 반환되기 전에 첫번째 호출이 발생 할 수 있습니다.)

고품질 이미지가 준비되면, Photos에서는 resultHandler를 다시 호출하여 이를 제공합니다.


image manager가 이미 요청한 이미지를 최고 품질로 캐시한경우, Photos에서는 resultHandler를 한번만 호출합니다.

resultHandler의 info 파라미터에 있는 "PHImageResultIsDegradedKey" key는 Photos가 일시적인 저품질 이미지를 제공하는 시기를 제공합니다.

이미지 asset과 비디오 asset 모두에 이 메소드를 사용 할 수 있습니다. 

비디오 asset의 경우, 이미지 요청은 썸네일 이미지 또는 포스터 프레임(poster frame)을 제공합니다.


오,...알고있으면 유용할 정보들이 많이 있는 것 같아요!

일단 

비동기 요청의 경우, Photos는 resultHandler블록을 두번 이상 호출 할 수 있습니다.(ㅇㅎ..)

이것부터 진짠지 볼까여 



처음에 5는 이미지 갯수고...

진짜 2번 불리네여ㅛ 

신기하군



아무튼 간단하게 내 사진첩의 모든 이미지를 가져오는 걸 해봤는데...간단간단하게 여러가지를 하는 글을 계속 써보려고 합니다. 

그리고 오늘 한 프로젝트는 github에 올려놨으니 참고하세요. 

하핫



반응형