Published on

๐Ÿ“ฑ iOS - PHPicker

Authors
  • avatar
    Name
    ์ด์ฐฝ์ค€
    Twitter

ํ† ์ด ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋˜ ์ค‘์— ์ด๋ฏธ์ง€๋ฅผ ์„ ํƒํ•ด์„œ ์—…๋กœ๋“œํ•˜๋Š” ๋™์ž‘์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‹น์—ฐํžˆ ์ฒ˜์Œ์—๋Š” ์ž๋ฃŒ๊ฐ€ ๋งŽ์€ UIImagePickerController๋ฅผ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ๋•Œ๋Š” PHPicker๋ผ๋Š” API๊ฐ€ ์žˆ๋Š”์ง€๋„ ๋ชฐ๋ž์—ˆ์ฃ .

๊ธฐ๋Šฅ์ด ๊ต‰์žฅํžˆ ์ œํ•œ์ ์ด๋ผ๋Š” ์‚ฌ์‹ค์— ํ•œํƒ„ํ•˜๋ฉฐ ๊ณต๋ถ€๋ฅผ ํ•˜๋˜ ์ค‘์— iOS14์— ๊ธฐ์กด์˜ UIImagePickerController๋ฅผ ๋Œ€์ฒดํ•˜๊ณ ์ž PHPicker๊ฐ€ ๋“ฑ์žฅํ–ˆ๋‹ค๋Š” ์†Œ์‹์„ ์ ‘ํ•˜๊ฒŒ ๋์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ์‚ฌ์ง„ ์•ฑ์— ์žˆ๋Š” ๋‹ค์ค‘์„ ํƒ/์คŒ์ธ์•„์›ƒ/๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ์ด ํฌํ•จ๋˜์–ด ์žˆ๋‹ค๊ณ  ํ•˜๋„ค์š”..! ๐Ÿซข

PhotoKit

์‚ฌ์‹ค ์ด PHPicker ๋ผ๋Š” ๋†ˆ.. ํ˜ผ์ž ๋–กํ•˜๋‹ˆ ๋“ฑ์žฅํ–ˆ๋˜ ๊ฑด ์•„๋‹™๋‹ˆ๋‹ค.

์• ํ”Œ์ด ๋ผ์ด๋ธŒ ํฌํ†  ๋“ฑ์„ ์ถ”๊ฐ€ํ•˜๋ฉฐ ์‚ฌ์ง„๊ณผ ๋น„๋””์˜ค ๊ธฐ๋Šฅ์— ํž˜์„ ์ฃผ๊ธฐ ์œ„ํ•ด ๊ฐœ๋ฐœํ•œ PhotoKit์— ํฌํ•จ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.

์—ญ์‹œ ๊ณต์‹ ๋ฌธ์„œ๊ฐ€ ์ •๋ณด๋ฅผ ์ฐพ๊ธฐ์—” ๊ฐ€์žฅ ์ข‹๊ฒ ์ฃ ?

Technology - PhotoKit

์ƒˆ๋กœ์šด API๋ฅผ ์†Œ๊ฐœํ•˜๋Š” WWDC์—์„œ๋„ ๋‹น์—ฐํžˆ ์†Œ๊ฐœํ–ˆ๋˜ ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

WWDC2020 - Meet the new Photos picker

์„ธ์…˜ ์ œ๋ชฉ์—๋„ **โ€œPhotosโ€**๋ผ๊ณ  ๊ฐ•์กฐ๋ฅผ ํ•ด๋‘์—ˆ๋„ค์š”.

๊ฐœ๋ฐœ์ž๋“ค์ด ์–ผ๋งˆ๋‚˜ ์—ฌ๋Ÿฌ ์‚ฌ์ง„ ์„ ํƒ์„ ์š”๊ตฌํ–ˆ์—ˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๋Œ€๋ชฉ์ด ์•„๋‹Œ๊ฐ€ ์‹ถ์Šต๋‹ˆ๋‹ค.

์‚ด์ง์ฟต 2021๋…„๊ณผ 2022๋…„ ์„ธ์…˜์—์„œ๋„ ์†Œ๊ฐœ๋œ ๋‚ด์šฉ์„ ์‚ดํŽด๋ณด๋‹ˆ, 2021๋…„์—๋Š” ProRAW ํฌ๋งท ์†Œ๊ฐœ์— ํž˜์„ ์ค€ ๋งŒํผ ์ƒˆ๋กœ์šด ํฌ๋งท ์ง€์›์„ ์†Œ๊ฐœํ–ˆ๊ณ  2022๋…„์—๋Š” ์‹ค๋ฆฌ์ฝ˜ ๋งฅ๊ณผ ํ•จ๊ป˜ ๋–ก์ƒํ•œ macOS์—์„œ๋„ PHPicker๋ฅผ ์ œ๋Œ€๋กœ ์ง€์›ํ•œ๋‹ค๋Š” ๋‚ด์šฉ๋“ค์ด ์žˆ๋„ค์š”.

์ด๋ฏธ ๊ธฐ์กด์˜ UIImagePicker๋ฅผ ์™„์ „ํžˆ ๋Œ€์ฒดํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ž ๊ทธ๋ž˜์„œ ํ•ต์‹ฌ์€ ๋ญ๋ƒ..?

PHPicker๋Š” PhotoKit ์ค‘์—์„œ๋„ PhotosUI ํ”„๋ ˆ์ž„์›Œํฌ์— ํฌํ•จ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉํ•˜๋ ค๋ฉด PhotosUI๋ฅผ importํ•ด์•ผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import PhotosUI

Photos Picker์˜ ์ด์ 

๊ทธ๋ž˜์„œ ์• ํ”Œ์€ ์™œ Photos Picker(PHPicker)๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๊ณ  ํ• ๊นŒ์š”?

๊ธฐ์กด์˜ UIImagePicker์— ๋Œ€์‘๋˜๋Š” 6๊ฐ€์ง€ ์ด์ ์„ ๋‚ด์„ธ์šฐ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ์ด๋ฏธ์ง€ ๋กœ๋”ฉ์˜ ์ง€์—ฐ๊ณผ ๋ณต๊ตฌ UI
  2. RAW์™€ ํŒŒ๋…ธ๋ผ๋งˆ ์ด๋ฏธ์ง€ ๋“ฑ์˜ ํฌ๊ณ  ๋ณต์žกํ•œ ์• ์…‹์„ ์•ˆ์ „ํ•˜๊ฒŒ ํ•ธ๋“ค๋ง
  3. UIImagePickerController์—์„œ๋Š” ๋ถˆ๊ฐ€๋Šฅํ•œ ์œ ์ € ์„ ํƒ ์˜ต์…˜
  4. ๋ผ์ด๋ธŒ ํฌํ† ๋งŒ์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š” ์„ค์ • ์ œ๊ณต
  5. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ ‘๊ทผ ์—†์ด PHLivePhoto ๊ฐ์ฒด ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  6. ์œ ํšจํ•˜์ง€ ์•Š์€ ์ž…๋ ฅ์— ๋Œ€ํ•œ ๊นŒ๋‹ค๋กœ์šด validation

๊ธฐ์กด์˜ ์•ฑ๋“ค์€ ํ•ธ๋“œํฐ์— ์ €์žฅ๋œ ์‚ฌ์ง„๋“ค์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ ‘๊ทผ ์š”์ฒญ์„ ํ–ˆ์—ˆ์ฃ ?

์œ„ ์‚ฌ์ง„์ฒ˜๋Ÿผ์š”.

ํ•˜์ง€๋งŒ ์ƒˆ๋กœ์šด PHPicker๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ”„๋กœํ•„ ์‚ฌ์ง„ ์„ ํƒ๋“ฑ์˜ ๋‹จ์ˆœํ•œ ์‚ฌ์ง„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž‘์—…์œผ๋กœ๋Š” ๊ถŒํ•œ์„ ์š”๊ตฌํ•˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์ง„ ํŽธ์ง‘, ์นด๋ฉ”๋ผ ์•ฑ๋“ฑ์€ ์—ฌ์ „ํžˆ ๊ถŒํ•œ์„ ์š”๊ตฌํ•œ๋‹ค๊ณ  ํ•˜๋„ค์š”.

๋˜ํ•œ ์‚ฌ์ง„์„ ๋‹จ์ˆœํžˆ ์ €์žฅํ•˜๋Š” ์ž‘์—… ๋˜ํ•œ ์‚ฌ์ง„ ์ €์žฅ ๊ถŒํ•œ์ด๋ผ๋Š” ๋‚ฎ์€ ๋ ˆ๋ฒจ์˜ ๊ถŒํ•œ๋งŒ์„ ๋ถ€์—ฌํ•ด์„œ ์‚ฌ์šฉ์ž์˜ ์•„์ดํฐ์„ ๋” ์•ˆ์ „ํ•˜๊ฒŒ ์ง€ํ‚ฌ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

๊ฐ€์žฅ ์ค‘์š”ํ•œ ์ ์€ ๋ณด์•ˆ์—์„œ์ž…๋‹ˆ๋‹ค.

PHPicker๋Š” ์†Œํ”„ํŠธ์›จ์–ด์ ์ธ ์บก์ฒ˜์—์„œ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

์Šคํฌ๋ฆฐ์ƒท์„ ์ฐ์„ ๋•Œ ๋ชจ๋“  ์ปจํ…์ธ (์‚ฌ์ง„, ๋น„๋””์˜ค)๋“ค์„ ๋ Œ๋”๋งํ•˜์ง€ ์•Š์€ ์ƒํƒœ๋กœ ์บก์ฒ˜ํ•˜์—ฌ ๋ถˆ๋ฒ• ํ”„๋กœ๊ทธ๋žจ์œผ๋กœ ์ธํ•ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์บก์ฒ˜๋˜์–ด ๊ฐœ์ธ ์ •๋ณด๊ฐ€ ์œ ์ถœ๋˜๋Š” ๊ฒƒ์„ ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒŒ ์–ด๋–ป๊ฒŒ ๊ฐ€๋Šฅํ•œ ๊ฒƒ์ด๋ƒ?

PHPicker๋Š” ์•ฑ ์•ˆ์—์„œ ์‹คํ–‰๋˜๋Š” ๊ฒƒ ๊ฐ™์€ UI๋ฅผ ๊ฐ–์ง€๋งŒ ์‚ฌ์‹ค์„ ๋…๋ฆฝ๋œ ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค์ž…๋‹ˆ๋‹ค. ์œ„ ์‚ฌ์ง„์—์„œ์ฒ˜๋Ÿผ์š”.

์•ฑ์˜ ๋ชจ์–‘์„ ๊ทธ์ € ๋ Œ๋”๋งํ•˜์—ฌ ๋ฐฐ๊ฒฝ์œผ๋กœ ๋ณด์—ฌ์ฃผ๊ณ  ์žˆ๋Š” ๊ฒƒ์ด์ฃ .

๋”ฐ๋ผ์„œ ์•ฑ์€ PHPicker์˜ ๊ธฐ๋Šฅ๋“ค์„ ์ง์ ‘ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, PHPicker๋Š” ๋”ฐ๋กœ ๋™์ž‘ํ•˜๊ณ  ์œ ์ €๊ฐ€ ์„ ํƒํ•œ ์ปจํ…์ธ ๋“ค์— ํ•œํ•ด์„œ๋งŒ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

PHPickerViewController

์œ„ ์‚ฌ์ง„์—์„œ ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด PHPickerViewController๋Š” PHPickerConfiguration์„ ํ†ตํ•ด ์ƒ์„ฑ๋˜๊ณ  ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

var configuration = PHPickerConfiguration()

filter์™€ selectionLimit

filter ํ”„๋กœํผํ‹ฐ๋กœ๋Š” PHPicker์—์„œ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š” ์ปจํ…์ธ  ํƒ€์ž…์„ ์ •ํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

images, livePhotos, videos, screenshots ๋“ฑ ๋‹ค์–‘ํ•œ ์˜ต์…˜์„ ์ง€์›ํ•˜๊ณ  ์žˆ๊ณ , ๋”ฐ๋กœ ์ง€์ •ํ•ด์ฃผ์ง€ ์•Š๋Š”๋‹ค๋ฉด ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ .all ๊ฐ’์„ ๊ฐ–์Šต๋‹ˆ๋‹ค.

configuration.filter = .images
configuration.filter = .any(of:
  [.images, .livePhotos, .videos]
)

selectionLimit ํ”„๋กœํผํ‹ฐ๋กœ๋Š” ์„ ํƒํ•  ์ด๋ฏธ์ง€์˜ ๊ฐœ์ˆ˜๋ฅผ ์ •ํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ๊ฐ’์€ 1์ด๊ณ , ์Œ์ˆ˜์˜ ๊ฐ’์„ ๋„ฃ์œผ๋ฉด ์•ฑ์ด ํฌ๋ž˜์‰ฌ๋‚ฉ๋‹ˆ๋‹ค.

numberOfLines์™€ ๊ฐ™์ด 0์˜ ๊ฐ’์„ ๋„ฃ์–ด์ฃผ๋ฉด ์„ ํƒ ๊ฐœ์ˆ˜๋ฅผ ์ œํ•œ ์—†์ด ์„ค์ •ํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

configuration.selectionLimit = 12

์ด๋ ‡๊ฒŒ ์„ค์ •ํ•ด์ค€ configuration ๊ฐ’์€ PHPickerViewController์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„ฃ์–ด์ฃผ์–ด ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

let imagePicker = PHPickerViewController(configuration: configuration)
imagePicker.delegate = self
present(imagePicker, aniamted: true)

UIImagePickerController์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ delegate๊นŒ์ง€ ์œ„์ž„ํ•ด์ฃผ๊ณ  presentํ•˜๋ฉด ์‚ฌ์ง„์„ ๊ณ ๋ฅผ ์ˆ˜ ์žˆ๋Š” ํ™”๋ฉด์ด present๋ฉ๋‹ˆ๋‹ค.

์„ ํƒ ๊ฒฐ๊ณผ ํ•ธ๋“ค๋ง

์œ ์ €๊ฐ€ ์ปจํ…์ธ ๋ฅผ ์„ ํƒํ–ˆ๋‹ค๋ฉด ํ•ด๋‹น ์ปจํ…์ธ ๋“ค๋กœ ๋ฌด์–ธ๊ฐ€๋ฅผ ํ•ด์•ผ๊ฒ ์ฃ .

PHPicker๋Š” delegate์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

PHPickerViewControllerDelegate ํ”„๋กœํ† ์ฝœ์„ ์ฑ„์šฉํ•ด์ฃผ๊ณ ,

extension DefaultImagePickerCoordinator: PHPickerViewControllerDelegate {

    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
    // do something
    }

}

์œ ์ €๊ฐ€ ์„ ํƒ์„ ์™„๋ฃŒํ–ˆ๋‹ค๋Š” ๋ฒ„ํŠผ์„ ํƒญํ•˜๋ฉด ํ˜ธ์ถœ๋˜๋Š” picker(_,didFinishPicking) ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•ด์ค๋‹ˆ๋‹ค.

func picker(_ picker: PHPickerViewController, didFinishPicking reuslts: [PHPickerResult]) {
  picker.dismiss(animated: true)

  let itemProvider = results.first?.itemProvider

  if let itemProvider = itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self) {
    itemProvider.loadObject(ofClass: UIImage.self) { image, error in
      if let image = image {
        // do something
      }
    }
  }
}

์šฐ์„  picker.dismiss(animated:)๋กœ PHPickerView๋ฅผ dismiss ํ•ด์ค๋‹ˆ๋‹ค.

๊ทธ ํ›„ NSItemProvider๋ผ๋Š” ๊ฐ์ฒด๋กœ๋ถ€ํ„ฐ ์œ ์ €๊ฐ€ ์„ ํƒํ•œ ์ปจํ…์ธ ๋ฅผ ์ œ๊ณต๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ์ดˆ๊ธฐํ™” ๊ณผ์ •๊ณผ ์‹ค์ œ๋กœ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ณผ์ •์„ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค.

์ด ๊ณผ์ •์€ ๋‚ด๋ถ€์ ์œผ๋กœ asyncํ•˜๊ฒŒ ๊ตฌํ˜„๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— @escaping ํด๋กœ์ € ์•ˆ์—์„œ ์ด๋ฏธ์ง€๋ฅผ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‹จ์ˆœํžˆ ์ด๋ฏธ์ง€๋‚˜ ๋น„๋””์˜ค๋ฅผ ์„ ํƒํ•ด์„œ ๋ฐ›์•„์˜ค๋Š” ์ž‘์—…์€ ์ด๊ฒƒ๋งŒ์œผ๋กœ๋„ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ๊นŒ์ง€์˜ ์ž‘์—…์€ ์‚ฌ์ง„ ์ ‘๊ทผ ๊ถŒํ•œ์„ ์š”๊ตฌํ•˜์ง€ ์•Š์ง€๋งŒ ์‚ฌ์ง„์„ ํŽธ์ง‘ํ•˜๋Š” ๋“ฑ์˜ ์ž‘์—…๋“ฑ์€ ์‚ฌ์ง„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ถŒํ•œ์„ ์š”๊ตฌํ•˜๊ณ , PhotoKit์˜ ๋‹ค๋ฅธ API๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ž‘์—…ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๋งˆ๋ฌด๋ฆฌ

์˜ค๋Š˜์€ ์šฐ์„  ์—ฌ๊ธฐ๊นŒ์ง€ ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค.

์• ํ”Œ์€ ์ด๋ฏธ UIImagePickerView๋ฅผ deprecated ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.

๊ตฌ๊ธ€ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋Š” ์•„์ง ์ด์ „ ๊ฒŒ์‹œ๋ฌผ๋“ค์„ ์ƒ๋‹จ์— ๋…ธ์ถœ์‹œํ‚ค๊ณ  ์žˆ์–ด WWDC ์„ธ์…˜์„ ์ฐพ์•„๋ณด์ง€ ์•Š์•˜๋‹ค๋ฉด ์—ฌ์ „ํžˆ ํ—ค๋งค๊ณ  ์žˆ์—ˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

ํ‹ˆํ‹ˆํžˆ WWDC ๊ตฌ์„๊ตฌ์„์„ ์‚ดํŽด๋ณด๋Š” ์Šต๊ด€์„ ๊ฐ€์ง‘์‹œ๋‹ค!