PhotoKit을 알아보자 - 2
(이 이미지는 왜 올라간 것일까.. 너 뭐야 ㅠ 나 안올렸다고)
이번에는 직접 Photo 프레임워크를 이용해서 유저의 사진 앨범에 접근을 해서 collectionView에 사진을 불러오거나 바로 카메라에 접근을 해서 이미지를 불러올 것이다.
콜렉션 뷰의 첫번째 아이템(카메라 이미지)을 선택하면 카메라로 넘어가고, 그 외 아이템들을 클릭하면 해당하는 이미지들을 가져올 수 있다.
중요! 앱이 유저의 카메라와, 앨범에 접근하려면 접근권한이 필요하다. info.plist로 이동한다.
다음과 같이 세 항목을 추가한다.
//MARK: - tempViewController
// 앨범에 접근해서 PHAsset 배열을 가져온 다음에 버튼을 눌러 뷰 전환을 할 때 collectioView에 넘겨줄 것이다.
import UIKit
import Photos
class tempVC: UIViewController {
let btn: 생략
var allPhotos: PHFetchResult<PHAsset>!
override func viewDidLoad() {
super.viewDidLoad()
//...//
fetchAsset()
}
private func fetchAsset() {
let allPhotoOptions = PHFetchOptions()
allPhotoOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
allPhotos = PHAsset.fetchAssets(with: allPhotoOptions)
}
private func tapButton() {
var navigationController: UINavigationController
let targetVC = testCollectionViewer
// 여기가 핵심. targetVC에 있는 변수인 fetchResult에 넘겨준다.
targetVC.fetchResult = allPhotos
navigationController = UINavigationController(rootViewController: targetVC)
navigationController.modalPresentationStyle = .fullScreen
present(navigationController, animated: true)
}
}
다음으로는, PHFetchResult을 넘겨받아서 collectionView에 띄어주는 viewController를 만들 것이다. collectionView 생성 및 화면 배치는 생략할 것이다.
이전 챕터에서 언급 했듯이, fetchResult의 결과는 메타데이터다. 우리는 실제 이미지를 받아와야하기 때문에, 이것을 기반으로 image를 요청해야한다. 그러기 위해서 PHImageManager라는 것이 필요하다.
PHImageManger에 구현되어 있는 method requestImage를 통해 이미지를 받아올 수 있다. 코드로 확인해보자.
//MARK: - testCollectionViewController
class testCollectionViewController: UIViewController {
// UIComponent
var pictureCV = UICollectionView().then {//이하 생략}
// Variable & properties
var fetchResult: PHFetchResutl<PHAsset>!
var photoImage: UIImage? // 여기에 카메라로 찍은 사진 저장
fileprivate let imageManager = PHImageManager()
fileprivate var thumbnailSize: CGSize!
//MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
//...생략 //
}
override func viewWillAppear(_ animated: Bool) {
let scale = UIScreen.main.scale
let cellSize = pictureCV.size
thumbnailSize = CGSize(width: cellSize.width * scale,
height: cellSize.height * scale)
}
}
extension testCollectionViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return fetchResult.count + 1
// 카메라 이미지를 넣어야 하기 때문에,
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let pictureCell =
collectionView.dequeueReusableCell(withReuseIdentifier: "PictureCell",
for: indexPath) as? PictureCell else
{ return UICollectionViewCell() }
if indexPath.item == 0 {
pictureCell.thumbnailImage = UIImage(systemName: "camera")
return pictureCell
} else {
let asset = fetchResult.object(at: (indexPath.item)-1 )
pictureCell.representedAssetIdentifier = asset.localIdentifier
imageManager.requestImage(for: asset, targetSize: thumbnailSize, contentMode: .aspectFill, options: nil, resultHandler: { image, _ in
if pictureCell.representedAssetIdentifier == asset.localIdentifier {
pictureCell.thumbnailImage = image
}
})
return pictureCell
}
}
}
asset은 unique한 localIdentifier를 가진다고 했는데 각각의 cell에 이 localId를 넣어주는 게 중요하다. 왜냐하면 requestImage를 통해 가져오는 UIImage의 이름은 계속 바뀌기 때문이다. 그래서 데이터처리를 할 때 UIImage name으로 하는 것이 아니라 identifier로 처리를 해야만 한다.
cellClass는 다 적지는 않겠지만 이런 식으로 구성한다.
MARK: Cell Class
class PictureCell: UICollectionViewCell {
var photoImageView = UIImageView().then{
$0.backgroundColor = .black
}
var thumbnailImage: UIImage! {
didSet {
photoImageView.image = thumbnailImage
}
}
var representedAssetIdentifier: String!
//... 생략//
}
마지막으로 카메라 이미지를 선택했을 때 카메라가 present로 올라와서 찍은 이미지를 변수에 저장하는 것이다.
MARK: -
class testCollectionViewCotroller: UIViewController {
///...//
func didTapCamera() {
let camera = UIImagePickerController()
camera.sourceType = .camera
camera.cameraDevice = .rear
camera.cameraCaptureMode = .photo
camera.delegate = self
present(camera, animated: true, completion: nil)
}
}
extension testCollectionViewCotroller: UIImagePickerControllerDelegate & UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
// 미디어 종류 확인
let mediaType = info[UIImagePickerController.InfoKey.mediaType] as! NSString
// 미디어가 사진이면
if mediaType.isEqual(to: kUTTypeImage as NSString as String){
// 사진을 가져옴
let captureImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
// 사진을 포토 라이브러리에 저장. 안하려면 없애주면 된다.
UIImageWriteToSavedPhotosAlbum(captureImage, self, nil, nil)
photoImage = captureImage
}
// 현재의 뷰(이미지 피커) 제거
self.dismiss(animated: true, completion: {
self.didTapReset()
let targetVC = tempCV()
targetVC.photoImage = self.photoImage
self.navigationController?.pushViewController(targetVC, animated: true)
})
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.dismiss(animated: true, completion: nil)
}
}
이렇게 하면, 카메라에 접근할 수 있을 것이다. 다만 위의 영상에는 카메라를 누르지 않는데, 시뮬레이터이기 때문에 테스트를 해볼 수 없기 때문이다. 아이폰으로 테스트를 한다면 성공적으로 작동한다.
-끝-
Author And Source
이 문제에 관하여(PhotoKit을 알아보자 - 2), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@stupidchoco/PhotoKit을-알아보자-2저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)