Scroll View를 통해 이미지 확대해보기

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


Scroll View를 통해 이미지 확대해보기

우선 ImageZoomViewController 파일 하나를 생성해준다.

import UIKit
import Photos

class ImageZoomViewController: UIViewController, UIScrollViewDelegate {

    // 전화면에서 받아올 asset 프로퍼티
    var asset: PHAsset!
    // imageManager를 통해 이미지 요청
    let imageManager: PHCachingImageManager = PHCachingImageManager()

    @IBOutlet weak var imageView: UIImageView!

    // scrollView delegate 메서드
    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        // scrollview가 줌을 해줄 대상이 누구? = imageView
        return self.imageView
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        // 메모리에 화면이 로드가 되면 호출될 이미지의 사이즈 등..
        imageManager.requestImage(for: asset, targetSize: CGSize(width: asset.pixelWidth, height: asset.pixelHeight), contentMode: .aspectFill, options: nil, resultHandler: { image, _ in self.imageView.image = image})

        // Do any additional setup after loading the view.
    }
}

스토리보드로 넘어서 새로운 뷰컨트롤러를 만들어주고 이전에 만들어놨던 뷰컨트롤러는 네비게이션 컨트롤러 임베드를 해준다.

그리고 스크롤뷰의 delegate는 ImageZoomViewController가 되고 ImageZoomViewController의 imageView는 스토리보드에 생성해놓은 imageView로 연결해준다.

그리고 스크롤뷰와 이미지뷰의 셋팅을 위와같이 설정해준다.

그리고 ViewController로 넘어와 네비게이션 컨트롤러를 통해 전달해줄 데이터 세그를 작성해준다.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    guard let nextViewController: ImageZoomViewController = segue.destination as? ImageZoomViewController else { return }
    guard let cell: UITableViewCell = sender as? UITableViewCell else { return }
    guard let index: IndexPath = self.tableView.indexPath(for: cell) else { return }
    nextViewController.asset = self.fetchResult[index.row]
}

Scroll View

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


Scroll View

스크롤뷰는 스크롤뷰 안에 포함된 뷰를 상,하,좌,우로 스크롤 할 수 있고 확대 및 축소할 수 있는 뷰를 의미한다.

그리고 스크롤뷰를 상속받아 활용되는 뷰로는 UITableView, UICollectionView, UITextView등 여러 UIKit 클래스가 있다.

스크롤뷰 상호작용

주요 프로퍼티

delegate : 스크롤뷰 객체의 델리게이트

weak var delegate: UIScrollViewDelegate? { get set }

UIScrollViewDelegate 프로토콜에 의해 선언된 메소드 델리게이트가 UIScrollView 클래스의 메시지에 응답

콘텐츠 크기 및 오프셋 관리

주요 프로퍼티

contentSize : 콘텐츠뷰의 크기

var contentSize: CGSize { get set }

contentOffset : 콘텐츠뷰의 원점이 스크롤뷰의 원점에서 오프셋 된 지점

var contentOffset: CGPoint { get set }

주요 메서드

// setContentOffset(_:animated:) : 스크롤뷰의 원점에 대한 콘텐츠뷰의 오프셋 설정
func setContentOffset(_ contentOffset: CGPoint, animated: Bool)

콘텐츠 삽입 동작 관리

주요 프로퍼티

contentInset : 콘텐츠뷰와 안전 영역 또는 스크롤뷰 가장자리에 간격

var contentInset: UIEdgeInsets { get set }

스크롤뷰 구성

주요 프로퍼티

// isScrollEnabled : 스크롤링이 사용 가능한지 아닌지를 결정하는 부울 값
var isScrollEnabled: Bool { get set }

// isDirectionalLockEnabled : 스크롤이 특정 방향으로 고정할지를 결정하는 부울 값
var isDirectionalLockEnabled: Bool { get set }

//isPagingEnabled : 스크롤뷰에서 페이징을 사용할 수 있는 여부를 결정하는 부울 값
var isPagingEnabled: Bool { get set }

//scrollsToTop :  스크롤 할 수 있는 제스처를 사용할지를 결정하는 부울 값
var scrollsToTop: Bool { get set }

// bounces : 스크롤뷰가 가장자리를 통과해서 다시 튀어나오는지 제어하는 부울 값
var bounces: Bool { get set }

// alwaysBounceVertical : 세로 스크롤이 콘텐츠뷰의 끝에 도달할 때 튀어 오르기가 항상 발생하는지를 결정하는 부울 값
var alwaysBounceVertical: Bool { get set }

// alwaysBounceHorizontal : 가로 스크롤이 콘텐츠뷰의 끝에 도달할 때 튀어 오르기가 항상 발생하는지를 결정하는 부울 값
var alwaysBounceHorizontal: Bool { get set }

스크롤링 상태 가져오기

주요 프로퍼티

// isTracking : 사용자가 스크롤을 시작하기 위해 콘텐츠를 터치한 여부를 반환
var isTracking: Bool { get }

// isDragging : 사용자가 콘텐츠를 스크롤하고 있는지 나타내는 부울 값
var isDragging: Bool { get }

// isDecelerating : 사용자가 손가락을 떼었을 때 콘텐츠가 스크롤뷰에서 움직이지 않고 있는지를 반환
var isDecelerating: Bool { get }

// decelerationRate : 사용자가 손가락을 뗀 후의 감속도를 결정하는 부동 소수점 값
var decelerationRate: CGFloat { get set }

스크롤 인디케이터 및 새로고침 제어 관리

주요 프로퍼티

// indicatorStyle : 스크롤 인디케이터의 스타일
var indicatorStyle: UIScrollViewIndicatorStyle { get set }

// showsHorizontalScrollIndicator : 가로 스크롤 바 표시 여부를 제어하는 부울 값
var showsHorizontalScrollIndicator: Bool { get set }

// showsVerticalScrollIndicator : 세로 스크롤 바 표시 여부를 제어하는 부울 값
var showsVerticalScrollIndicator: Bool { get set }

특정 위치로 스크롤 하기

주요 메서드

// scrollRectToVisible(_:animated:) : 콘텐츠의 특정 위치로 스크롤 하여 화면에 표시
func scrollRectToVisible(_ rect: CGRect,  animated: Bool)

확대 및 축소

주요 프로퍼티

// panGestureRecognizer : 팬 제스처를 제어하기 위한 제스처 인스턴스
var panGestureRecognizer: UIPanGestureRecognizer { get }

// pinchGestureRecognizer : 핀치 제스처를 제어하기 위한 제스처 인스턴스
var pinchGestureRecognizer: UIPinchGestureRecognizer? { get }

// zoomScale : 스크롤뷰 콘텐츠에 적용되는 현재 배율
var zoomScale: CGFloat { get set }

// maximumZoomScale :  스크롤뷰 콘텐츠에 적용되는 최대 배율
var maximumZoomScale: CGFloat { get set }

// minimumZoomScale : 스크롤뷰 콘텐츠에 적용되는 최소 배율
var minimumZoomScale: CGFloat { get set }

// isZoomBouncing : 확대 및 축소가 지정한 배율 제한을 초과했음을 나타내는 부울 값
var isZoomBouncing: Bool { get }

// isZooming : 콘텐츠뷰가 현재 확대 또는 축소되어 있는지를 나타내는 부울 값
var isZooming: Bool { get }

// bouncesZoom : 크기 조정이 최대 또는 최소 제한을 초과할 때 튀어 오르는 애니메이션을 보여줄지 결정하는 부울 값
var bouncesZoom: Bool { get set }

주요 메서드

// zoom(to:animated:) : 콘텐츠 특정 영역 확대
func zoom(to rect: CGRect, animated: Bool)

// setZoomScale(_:animated:) : 현재 배율을 지정
func setZoomScale(_ scale: CGFloat, animated: Bool)

키보드 관리

주요 프로퍼티

// keyboardDismissMode : 스크롤뷰에서 드래그가 시작될 때 키보드가 해제되는 방식
var keyboardDismissMode: UIScrollViewKeyboardDismissMode { get set }

UIScrollViewDelegate 프로토콜

스크롤 및 드래그

// scrollViewDidScroll(_:) : 콘텐츠뷰를 스크롤 할 때 델리게이트에 알림
optional func scrollViewDidScroll(_ scrollView: UIScrollView)

// scrollViewWillBeginDragging(_:) : 스크롤뷰에서 콘텐츠 스크롤을 시작할 시점을 델리게이트에 알림
optional func scrollViewWillBeginDragging(_ scrollView: UIScrollView)

// scrollViewWillEndDragging(_:withVelocity:targetContentOffset:) : 스크롤뷰의 드래그가 끝나기 직전에 델리게이트에 알림
optional func scrollViewWillEndDragging(_ scrollView: UIScrollView,
                        withVelocity velocity: CGPoint,
                 targetContentOffset: UnsafeMutablePointer<CGPoint>)

// scrollViewDidEndDragging(_:willDecelerate:) : 스크롤뷰의 드래그가 끝났을 때 델리게이트에 알림
optional func scrollViewDidEndDragging(_ scrollView: UIScrollView,
                     willDecelerate decelerate: Bool)

// scrollViewShouldScrollToTop(_:) : 스크롤뷰가 콘텐츠의 맨 위로 스크롤 해야 하는 경우 델리게이트에 동작 여부를 물어봄
optional func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool

// scrollViewDidScrollToTop(_:) : 스크롤뷰가 콘텐츠의 맨 위로 스크롤 되었음을 델리게이트에 알림
optional func scrollViewDidScrollToTop(_ scrollView: UIScrollView)

// scrollViewWillBeginDecelerating(_:) : 스크롤링 동작이 감속되기 시작하고 있다고 델리게이트에 알림
optional func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView)

// scrollViewDidEndDecelerating(_:) : 스크롤링 동작이 감속이 끝났을 때 델리게이트에 알림

// optional func scrollViewDidEndDecelerating(_ scrollView: UIScrollView)

확대 및 축소

//viewForZooming(in:) : 스크롤뷰에서 확대 및 축소를 할 때 확대 및 축소를 할 뷰 인스턴스를 요청
optional func viewForZooming(in scrollView: UIScrollView) -> UIView?

// scrollViewWillBeginZooming(_:with:) : 스크롤뷰의 콘텐츠 확대가 시작될 때 델리게이트에 알림
optional func scrollViewWillBeginZooming(_ scrollView: UIScrollView,
                                 with view: UIView?)

// scrollViewDidEndZooming(_:with:atScale:) : 스크롤뷰의 콘텐츠 확대가 완료될 때 델리게이트에 알림
optional func scrollViewDidEndZooming(_ scrollView: UIScrollView,
                              with view: UIView?,
                           atScale scale: CGFloat)

// scrollViewDidZoom(_:) : 스크롤뷰의 확대 및 축소 배율이 변경될 때 델리게이트에 알림
optional func scrollViewDidZoom(_ scrollView: UIScrollView)

스크롤 애니메이션

scrollViewDidEndScrollingAnimation(_:) : 스크롤뷰의 스크롤 애니메이션이 끝날  델리게이트에 알림
optional func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView)

동시성 프로그래밍과 비동기 프로그래밍

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


미리 알아둘 개념 정리

프로세서

프로세서는 하드웨어적인 측면에서 컴퓨터 내에서 프로그램을 수행하는 하드웨어 유닛이다. 대표적으로 중앙처리장치(Central Processing Unit - CPU)가 이에 속한다. 한 컴퓨터가 여러 개의 프로세서를 갖는다면 멀티 프로세서라고 말하고, 듀얼 프로세서라고 한다면 한 컴퓨터에 두 개의 프로세서가 운용되는 것을 의미한다.

코어

프로세서에서 코어는 주요 연산회로이다. 싱글코어는 말 그대로 하나의 연산회로가 내장되어있는 것이고 듀얼코어는 두 개의 연산회로가 내장된 것을 뜻한다. 또, 여러 개의 코어를 가진 프로세서를 멀티 프로세서라고 한다.

프로그램(Program)과 프로세스(Process)

프로그램은 일반적으로 보조기억 장치에 저장된 실행코드 즉, 생명이 없는 상태를 말한다. 프로세스는 프로그램을 구동하여 프로그램 자체와 프로그램의 상태가 메모리상에서 실행되는 작업 단위를 말한다. 동시에 여러 개의 프로세스를 운용하는 시분할 방식을 멀티태스킹이라고 하며 이러한 프로세스 관리는 운영체제에서 담당한다.

스레드(Thread)

스레드는 하나의 프로세스 내에서 실행되는 작업흐름의 단위를 말한다. 보통 한 프로세스는 하나의 스레드를 가지고 있지만, 프로세스 환경에 따라 둘 이상의 스레드를 동시에 실행할 수 있다. 이러한 방식을 멀티스레딩이라고 한다. 그리고 프로그램 실행이 시작될 때부터 동작하는 스레드를 메인 스레드라 하고 그 외에 나중에 생성된 스레드를 서브 스레드 또는 세컨더리 스레드라고 한다.

비동기(Asynchronous) 프로그래밍

프로그램의 주 실행 흐름을 멈추어서 기다리는 부분 없이 바로 다음 작업을 실행할 수 있게 하는 방식이다.

즉, 코드의 실행 결과 처리를 별도의 공간에 맡겨둔 뒤 결과를 기다리지 않고 바로 다음 코드를 실행하는 병렬처리 방식을 의미한다. 비동기 프로그래밍은 언어 및 프레임워크에서 지원하는 여러 방법으로 구현할 수 있다.

동시성(Concurrency) 프로그래밍

논리적인 용어로 동시에 실행되는 것처럼 보이는 것을 의미한다.

싱글 코어(멀티 코어에서도 가능)에서 멀티스레드를 동작시키기 위한 방식으로 멀티 태스킹을 위해 여러 개의 스레드가 번갈아 가면서 실행되는 방식이다. 동시성을 이용한 싱글 코어의 멀티 태스킹은 각 스레드들이 병렬적으로 실행되는 것처럼 보이지만 사실은 서로 번갈아 가면서 실행되고 있는 방식이다.

병렬성(Parallelism) 프로그래밍

물리적으로 동시에 정확히 동시에 실행되는 것을 의미한다.

멀티 코어에서 멀티 스레드를 동작시키는 방식으로 데이터 병렬성(Data Parallelism)과 작업 병렬성(Task Parallelism)으로 구분된다.

  • 데이터 병렬성: 전체 데이터를 나누어 서브 데이터들로 만든 뒤, 서브 데이터들을 병렬 처리해서 작업을 빠르게 수행하는 방법
  • 작업 병렬성 : 서로 다른 작업을 병렬 처리하는 방법

동시성(Concurrecny)과 병렬성(Parallelism) 차이

동시성 프로그래밍과 병렬성 프로그래밍 모두 비동기(Asynchronous) 동작을 구현할 수 있지만, 그 동작 원리가 다르다

  • 동시성(Concurrecny) : 통장을 만들러 온 N개의 대기열과 한 명 이상의 은행직원
  • 병렬성(Parallelism) : 통장을 만들러 온 N개의 대기열과 N명의 은행직원

즉, 동시성은 싱글코어 및 멀티코어에서 모두 구현할 수 있지만, 병렬성은 멀티 코어에서만 구현할 수 있다.

iOS 환경 동시성 프로그래밍 지원 종류

  • Grand Central Dispatch (GCD) : 멀티 코어와 멀티 프로세싱 환경에서 최적화된 프로그래밍을 할 수 있도록 애플이 개발한 기술
  • 연산 대기열 (Operation Queue) : 비동기적으로 실행되어야 하는 작업을 객체 지향적인 방법으로 사용
  • Thread : 멀티스레드 프로그래밍을 위한 애플에서 제공하는 스레드 클래스

사진첩에서 사진을 가져오고 삭제해보기(실습)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


사진첩에서 사진 가져오기


import UIKit
import Photos

class ViewController: UIViewController, UITableViewDataSource {

    // 사진 목록을 테이블뷰에 보여줄 것
    @IBOutlet weak var tableView: UITableView!
    var fetchResult: PHFetchResult<PHAsset>!
    // 가져온 에셋을 가지고 이미지를 로드해옴
    let imageManager: PHCachingImageManager = PHCachingImageManager()
    let cellIdentifier: String = "cell"

    func requestCollection() {
        // 사진찍으면 사진이 저장되는 카메라롤의 컬렉션을 가져옴
        let cameraRoll: PHFetchResult<PHAssetCollection> = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumUserLibrary, options: nil)

        guard let cameraRollColletction = cameraRoll.firstObject else {
            return
        }
        let fetchOptions = PHFetchOptions()
        // 최신순으로 사진을 sort
        fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
        // 그 결과를 fetchResult라는 프로퍼티로 가져옴
        self.fetchResult = PHAsset.fetchAssets(in: cameraRollColletction, options: fetchOptions)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        let photoAuthorizationStatus = PHPhotoLibrary.authorizationStatus()

        switch photoAuthorizationStatus {
        case .authorized:
            print("접근 허가됨")
            self.requestCollection()
            self.tableView.reloadData()
        case .denied:
            print("접근 불허")
        case .notDetermined:
            print("아직 응답하지 않음")
            PHPhotoLibrary.requestAuthorization({ (status) in
                switch status {
                case .authorized:
                    print("사용자가 허용함")
                    self.requestCollection()
                    self.tableView.reloadData()
                case .denied:
                    print("사용자가 불허함")
                default: break
                }
            })
        case .restricted:
            print("접근 제한")
        }
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.fetchResult?.count ?? 0
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // cell에 이미지를 하나씩 넣어줌
        let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: self.cellIdentifier, for: indexPath)
        // asset은 fetchresult에서 index에 해당
        let asset: PHAsset = fetchResult.object(at: indexPath.row)
        // imageManager를 통해 실질적인 이미지를 요청
        imageManager.requestImage(for: asset, targetSize: CGSize(width: 30, height: 30), contentMode: .aspectFill, options: nil, resultHandler: { image, _ in cell.imageView?.image = image})
        return cell
    }
}

이렇게 코드를 진행하게 되면 에러가 발생할 것이다.

reloadData는 메인 쓰레드에서만 동작해야한다. » operation queue 사용

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    let photoAuthorizationStatus = PHPhotoLibrary.authorizationStatus()

    switch photoAuthorizationStatus {
    case .authorized:
        print("접근 허가됨")
        self.requestCollection()
        self.tableView.reloadData()
    case .denied:
        print("접근 불허")
    case .notDetermined:
        print("아직 응답하지 않음")
        PHPhotoLibrary.requestAuthorization({ (status) in
            switch status {
            case .authorized:
                print("사용자가 허용함")
                self.requestCollection()
                OperationQueue.main.addOperation {
                    self.tableView.reloadData()
                }
            case .denied:
                print("사용자가 불허함")
            default: break
            }
        })
    case .restricted:
        print("접근 제한")
    }
}

사진첩에서 사진 삭제하기

import UIKit
import Photos

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, PHPhotoLibraryChangeObserver {
    // PHphotoobserver는 library에 변화가 생기면 감지를 하는 프로토콜

    // 사진 목록을 테이블뷰에 보여줄 것
    @IBOutlet weak var tableView: UITableView!
    var fetchResult: PHFetchResult<PHAsset>!
    // 가져온 에셋을 가지고 이미지를 로드해옴
    let imageManager: PHCachingImageManager = PHCachingImageManager()
    let cellIdentifier: String = "cell"

    func requestCollection() {
        // 사진찍으면 사진이 저장되는 카메라롤의 컬렉션을 가져옴
        let cameraRoll: PHFetchResult<PHAssetCollection> = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumUserLibrary, options: nil)

        guard let cameraRollColletction = cameraRoll.firstObject else {
            return
        }
        let fetchOptions = PHFetchOptions()
        // 최신순으로 사진을 sort
        fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
        // 그 결과를 fetchResult라는 프로퍼티로 가져옴
        self.fetchResult = PHAsset.fetchAssets(in: cameraRollColletction, options: fetchOptions)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        let photoAuthorizationStatus = PHPhotoLibrary.authorizationStatus()

        switch photoAuthorizationStatus {
        case .authorized:
            print("접근 허가됨")
            self.requestCollection()
            self.tableView.reloadData()
        case .denied:
            print("접근 불허")
        case .notDetermined:
            print("아직 응답하지 않음")
            PHPhotoLibrary.requestAuthorization({ (status) in
                switch status {
                case .authorized:
                    print("사용자가 허용함")
                    self.requestCollection()
                    OperationQueue.main.addOperation {
                        self.tableView.reloadData()
                    }
                case .denied:
                    print("사용자가 불허함")
                default: break
                }
            })
        case .restricted:
            print("접근 제한")
        }
        // 아래 한줄을 써줌으로써 photoLibrary가 변화될때마다 delegate메서드가 호출됨
        PHPhotoLibrary.shared().register(self)
    }

    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {  // row를 편집할수 있게 해주는 메서드, tableview row를 밀어서 삭제하는 행위를 가능하게 함
        return true
    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        // 삭제 모드로 들어왔을때(편집을 하려할때)
        if editingStyle == .delete { // 삭제라면 asset을 가지고 delete를 하면
            let asset: PHAsset = self.fetchResult[indexPath.row]
        PHPhotoLibrary.shared().performChanges({PHAssetChangeRequest.deleteAssets([asset] as NSArray)}, completionHandler: nil)
            // 실제 삭제를 하게되면 나오게 되는 팝업창
        }
    }

    func photoLibraryDidChange(_ changeInstance: PHChange) {
        // observer 프로토콜을 따른 메서드

        guard let changes = changeInstance.changeDetails(for: fetchResult) else {
            return
        }
        // 어떤게 바꼈는지
        fetchResult = changes.fetchResultAfterChanges
        // 바꼈다면 tableview를 다시 불러달라
        OperationQueue.main.addOperation {
            self.tableView.reloadSections(IndexSet(0...0), with: .automatic)
        }
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.fetchResult?.count ?? 0
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // cell에 이미지를 하나씩 넣어줌
        let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: self.cellIdentifier, for: indexPath)
        // asset은 fetchresult에서 index에 해당
        let asset: PHAsset = fetchResult.object(at: indexPath.row)
        // imageManager를 통해 실질적인 이미지를 요청
        imageManager.requestImage(for: asset, targetSize: CGSize(width: 30, height: 30), contentMode: .aspectFill, options: nil, resultHandler: { image, _ in cell.imageView?.image = image})
        return cell
    }
}

Photos 프레임워크

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


Photos 프레임워크

Photos 프레임워크는 iOS 및 macOS에서 사진 애플리케이션, 사진 확장 기능을 지원하는 클래스를 제공한다. Photos 프레임워크를 통해 iOS 및 tvOS에서 iCloud 사진 라이브러리를 포함하여 사진 및 비디오에 직접 접근할 수 있다. 이 프레임워크를 사용하여 화면에 표시 및 재생할 에셋을 검색하고 이미지 또는 비디오를 편집하거나 앨범, 특별한 순간 및 iCloud 공유 앨범과 같은 에셋을 사용하여 작업할 수 있다.

특징 및 개념

Photos 프레임워크에는 iOS 및 tvOS에서 사용자의 사진 라이브러리와 직접 작업하기 위한 여러 가지 기능이 있다.

에셋

에셋 컬렉션(컬렉션)

컬렉션 리스트

  • 객체 가져오기 및 변경요청
    • Photos 프레임워크 모델 클래스 (PHAsset, PHAssetCollection, PHCollectionList)의 인스턴스는 사진 애플리케이션에서 에셋(이미지, 비디오, 라이브 포토), 에셋 컬렉션(앨범, 특별한 순간) 및 사용자가 작업하는 항목을 나타낸다. 그리고 컬렉션 리스트(앨범 폴더, 특별한 순간)다. 이 객체는 읽기 전용이며 변경할 수 없고 메타 데이터만 포함한다.
    • 해당 객체를 사용하여 작업해야 하는 데이터를 가져와서 에셋 및 컬렉션 작업을 할 수 있습니다. 변경 요청을 하려면 변경 요청 객체를 만들고 이를 공유 PHPhotoLibrary 객체에 명시적으로 알려줍니다. 이 아키텍처를 사용하면 다수의 스레드 혹은 다수의 애플리케이션과 동일한 에셋을 사용하여 쉽고 안전하며 효율적으로 작업할 수 있습니다.
  • 변경을 관찰
    • 가져온 에셋 및 컬렉션에 대한 변경 핸들러를 등록하려면 공유 PHPhotoLibrary 객체를 사용한다.
    • 다른 애플리케이션이나 기기가 에셋의 콘텐츠나 메타 데이터 또는 컬렉션의 리스트를 변경할 때마다 애플리케이션에 알려준다.
    • PHChange 객체는 변경 전후의 객체 상태에 대한 정보를 제공하여 쉽게 컬렉션뷰 또는 유사한 인터페이스를 업데이트할 수 있도록 한다.
  • Photos 애플리케이션의 기능들을 지원
    • PHCollectionList 클래스를 사용해 사진 애플리케이션의 특별한 순간 계층에 해당하는 에셋을 찾는다. 버스트, 파노라마 사진 및 고프레임 비디오를 식별하려면 PHAsset 클래스를 사용
    • iCloud 사진 라이브러리가 활성화되면 Photos 프레임워크의 에셋과 컬렉션에는 동일한 iCloud 계정의 사용할 수 있는 내용이 반영된다.
  • 에셋과 미리보기 로딩 및 캐싱
    • PHImageManager 클래스를 사용해 지정된 크기로 에셋의 이미지를 요청하거나 비디오 에셋에 사용할 AVFoundation 객체를 요청
  • 에셋 콘텐츠 편집
    • PHAsset 및 PHAssetChangeRequest 클래스는 편집을 위해 사진 또는 비디오를 요청하여 사진 라이브러리에 편집한 내용을 반영하는 메서드를 정의

Photos 라이브러리 상호작용

PHPhotoLibrary 객체를 사용하여 사진 콘텐츠에 접근하고, 에셋 및 컬렉션을 변경할 수 있도록 애플리케이션의 사용자 권한을 얻는다.
권한을 얻으면 사진 라이브러리가 변경될 때 변경사항을 전달받을 수도 있다.

PHPhotoLibrary: 사용자의 사진 라이브러리에 대한 접근 및 변경을 관리하는 공유 객체

에셋 검색과 조사

이 모델 클래스는 사진 라이브러리의 콘텐츠(에셋, 컬렉션)을 나타낸다. 읽기 전용이며 변경 불가능하며 메타 데이터만 포함되고, 에셋과 컬렉션을 사용하려면 이 클래스를 사용하여 지정한 쿼리와 일치하는 객체를 가져오게 된다.

  • PHAsset: 사진 라이브러리의 이미지, 비디오, 라이브 포토를 나타냄
  • PHAssetCollection: 특별한 순간, 사용자정의 앨범 또는 스마트 앨범과 같은 사진, 에셋 그룹을 나타냄
  • PHCollectionList: 특별한 순간, 사용자정의 앨범, 특별한 순간들 연도와 같은 에셋 컬렉션이 포함된 그룹을 나타냄
  • PHCollection: 에셋 컬렉션 및 컬렉션 리스트의 추상 수퍼 클래스
  • PHObject: 모델 객체(에셋 및 컬렉션)의 추상 수퍼 클래스
  • PHFetchResult: 가져오기 메서드에서 반환된 에셋 또는 컬렉션의 정렬된 목록
  • PHFetchOptions: 에셋 또는 컬렉션 객체를 가져올 때 Photos에서 반환하는 결과에 필터링, 정렬 등 영향을 주는 옵션

에셋 콘텐츠 로딩

이 클래스를 사용하여 이미지, 비디오, 라이브 포토 콘텐츠를 요청할 수 있다

  • PHImageManager: 미리보기 썸네일 및 에셋과 전체 크기의 이미지 또는 비디오 데이터를 검색하거나 생성하는 방법을 제공
  • PHCachingImageManager: 많은 에셋을 일괄적으로 미리 로딩하기 위해 최적화된 에셋과 관련된 섬네일 및 전체 크기의 이미지 또는 비디오 데이터를 검색하거나 생성하는 방법을 제공
  • PHImageRequestOptions: 이미지 매니저로부터 요청한 에셋 이미지의 영향을 주는 옵션들
  • PHVideoRequestOptions: 이미지 매니저로부터 요청한 비디오 에셋 데이터의 영향을 주는 옵션들
  • PHLivePhotoRequestOptions: 이미지 매니저로부터 요청한 라이브 포토 에셋의 영향을 주는 옵션들
  • PHLivePhoto: 캡처 직전과 직후 순간의 움직임 및 소리가 포함된 라이브 사진을 표현

변경 요청

에셋이나 컬렉션을 변경하려면 변경 요청 객체를 만들고 명시적으로 사진 라이브러리에 반영한다.
이 방법을 사용하면 여러 스레드 또는 여러 애플리케이션 및 애플리케이션 확장에서 같은 에셋을 가지며 쉽고, 안전하며 효율적으로 작업할 수 있다.

  • PHAssetChangeRequest: 사진 라이브러리 변경 블록(클로저)에서 사용하기 위해 에셋의 생성, 삭제, 메타 데이터 수정할 변경 요청 객체
  • PHAssetCollectionChangeRequest: 사진 라이브러리 변경 블록(클로저)에서 사용하기 위해 에셋 컬렉션을 생성, 삭제, 수정할 변경 요청 객체
  • PHCollectionListChangeRequest: 사진 라이브러리 변경 블록(클로저)에서 사용하기 위해 컬렉션 리스트 생성, 삭제, 수정할 변경 요청 객체

에셋 콘텐츠 수정

애플리케이션 또는 확장 프로그램에서 이 클래스들을 사용하여 사진 라이브러리의 편집 및 반영을 위해 에셋 데이터에 접근한다. 사진들은 각 수정 사항을 버전별로 에셋 및 보정 데이터를 관리 하므로 애플리케이션 또는 확장 프로그램을 사용하여 다른 기기에서도 이전에 수정한 내용을 되돌리거나 계속 사용할 수 있다. 사진 편집 확장기능을 만들려면 이 클래스들과 PhotosUI 프레임워크와 같이 사용!!

  • PHContentEditingInput: 편집할 에셋의 이미지, 비디오, 라이브 포토의 콘텐츠에 대한 정보와 접근 권한을 제공하는 컨테이너
  • PHContentEditingOutput: 에셋의 사진, 비디오, 라이브 포토의 콘텐츠를 편집한 결과를 제공하는 컨테이너
  • PHAdjustmentData: 편집 효과를 재구성하거나 되돌릴 수 있는 에셋의 사진, 비디오, 라이브 포토 콘텐츠의 수정사항에 대한 설명

Adjustment Data

  • PHContentEditingInputRequestOptions: 에셋의 콘텐츠를 수정하도록 요청할 때 이미지, 비디오 데이터전송에 영향을 주는 옵션
  • PHLivePhotoEditingContext: 라이브 포토의 사진, 비디오, 오디오 콘텐츠를 수정하기 위한 편집 세션
  • PHLivePhotoFrame: 편집 컨텍스트에서 라이브 포토의 단일 프레임에 대한 이미지 콘텐츠를 제공하는 컨테이너

변경사항 관찰

Photos 프레임워크는 다른 애플리케이션이나 다른 기기에서 사진의 정보를 변경할 때마다 애플리케이션에 알려준다. 이러한 객체는 변경 전후의 객체 상태에 대한 정보를 제공하므로 사용자 인터페이스를 쉽게 업데이트하여 일치시킬 수 있다.

  • PHPhotoLibraryChangeObserver: 사진 라이브러리에서 발생한 변경사항을 알리기 위해 구현할 수 있는 프로토콜
  • PHChange: 사진 라이브러리에서 발생한 변경사항에 대한 설명
  • PHObjectChangeDetails: 에셋 또는 컬렉션 객체에서 발생한 변경사항에 대한 설명
  • PHFetchResultChangeDetails: 가져오기 결과에 나열된 에셋 또는 컬렉션 객체에서 발생한 변경사항에 대한 설명

에셋 리소스로 작업하기

에셋 리소스 객체는 각 에셋의 데이터 저장소를 나타냅니다. 이러한 객체를 사용해 에셋을 직접 백업하고 복원할 수 있다.

  • PHAssetResource: 사진 라이브러리의 사진, 비디오, 라이브 포토 에셋과 관련된 기본 데이터 리소스
  • PHAssetCreationRequest: 사진 라이브러리 변경 블록(클로저)에서 사용하기 위해 기본 데이터 리소스에서 새로운 에셋을 생성하라는 요청
  • PHAssetResourceCreationOptions: 기본 리소스에서 새로운 에셋을 만드는데 영향을 주는 옵션들
  • PHAssetResourceManager: 애샛과 관련된 리소스에 대한 기본 데이터 저장소에 접근하는 방법을 제공
  • PHAssetResourceRequestOptions: 에셋 리소스 관리자가 요청한 기본 에셋 데이터 전달에 영향을 주는 옵션