UIImagePickerControllerに代わるPHPickerViewControllerの紹介

WWDC 2020の[Meet the new Photos picker](https://developer.apple.com/videos/play/wwdc2020/10652/)を見たのでメモです。

※ここに出てくるスクリーンショットは、全て上記の動画のものです。

# 概要

UIImagePickerControllerに代わるピッカーの紹介。このPHPickerを使えばPhoto Libraryへの完全なアクセスがなくても写真や動画を選択することができる。

# PHPickerとは

ユーザのPhoto Library内の写真や動画にアクセスすることができるシステム標準のピッカー。

PHPickerには以下の特徴がある。

* 検索ができる
* グリッド表示時にズームイン/アウトができる
* 写真や動画にアクセスするのに一番良い方法
* まったく新しいデザイン
* 使いやすいAPI
* 複数選択ができる
* フィルター選択が可能

# プライバシーへの配慮

写真ピッカーを表示するときに直接Photos Libraryにアクセスを要求する必要がないので、許諾アラートを出す必要もない。

# 内部の仕組み

PHPickerはアプリ内部で動作しているように見えるが、実際は別プロセスで動いている。
![スクリーンショット 2020-07-02 10.16.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100736/4b64150c-a90d-7a61-158a-555a4b87e33d.png)
アプリはピッカーに直接アクセスすることも、ピッカーコンテンツのスクショを撮ることもできない。ユーザが選択したものだけ、アプリ側は知ることができる。

# 新しいAPI

PHPickerViewControllerはPHPickerFilterなどの設定を渡されて初期化を行う。ユーザが何かを選択したら、PHPickerは結果を保持しておく用のスペースを作成し、delegateに渡す。
![スクリーンショット 2020-07-02 11.06.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100736/624b957a-b5b0-4e93-1aae-d64ca7d3fe87.png)

# コード

まず設定について。選択上限はselectionLimitで指定できる。デフォルトでは1が設定されており、0にすると無制限にできる。アイテムタイプのフィルタリングはfilterで行うことができる。タイプ指定は1つでも、複数でもできる。
![スクリーンショット 2020-07-02 11.06.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100736/68ba4520-7bb3-2c1a-9544-2cb5fa9de636.png)

設定を詰め込んだらPHPickerViewControllerにその情報を渡して初期化する。あとはdelegateを設定して、presentで表示するだけ。
![スクリーンショット 2020-07-02 11.14.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100736/91ad7385-e26c-6c31-3cfa-2e0a82cb70b7.png)

ユーザがアイテムを選択し終わったらdidFinishPickingが呼ばれる。まずはピッカーを閉じ、次にNSItemProviderに結果を聞き、期待するオブジェクトがあったら処理をする。このItemProviderは非同期で、エラーが発生したときの処理もここで書く必要がある。
![スクリーンショット 2020-07-02 11.19.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100736/6ef89bd7-41c6-64c2-ce92-e00a43edfee9.png)

# デモ

（[サンプルコードページ](https://developer.apple.com/documentation/photokit/selecting_photos_and_videos_in_ios)にあるアプリを使用）

真ん中にUIImageViewのplaceholder、右上に＋ボタンがある。今は＋ボタンをタップしても何も起きない。
やるべきことは2ステップ。まずは1枚の画像をImageViewに表示できるように、＋ボタンがタップされたらピッカーを表示し、選択した写真を貼り付ける処理を書く。次のステップとして、画像が複数枚選択されたときは、画面をタップ時に次の画像を表示するような処理を書く。

受け取った画像（1枚）のハンドリング例は以下の通り。
![スクリーンショット 2020-07-02 12.59.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100736/00db44b7-0ff1-f035-5fe9-977f1c5f55b8.png)

次に複数選択対応をする。itemProvidersで取得したアイテム全てが、iteratorで現在表示されている画像情報がわかるようにする。
![スクリーンショット 2020-07-02 14.16.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100736/d51ff2c7-5326-1c5f-0539-9acc162fbb55.png)
次の画像を表示するメソッドはこのように書く。
![スクリーンショット 2020-07-02 15.36.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100736/57ea7fdb-9933-042b-7932-cd4e6bd7489d.png)
単一選択と異なるのはiteratorを使っているかどうかと、delegateの部分。
![スクリーンショット 2020-07-02 15.49.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100736/0e7f1927-f2e8-29bb-1fee-f6c41b1e1d86.png)
実行するとこのような表示になる。画面下部のボタンをタップすると、今どんな写真がステージされているかがわかる。全ての写真を最初にロードしているわけではないので、読み込み速度は単一のときと変わらない。
![スクリーンショット 2020-07-02 15.51.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100736/4ed75984-ac75-df41-5125-355d47f7ee66.png)

# PHPicker x PhotoKit

PhotoKitベースのアプリの中には、PHAssetsを取得したいと思っているかもしれない。
PhotoKitを使うことでメリットが得られるアプリは、元画像を保持しつつ画像編集を行うアプリや、Photo Libraryの機能を使ったりするアプリ。とはいえPhotos Libraryへのアクセスは必要な場合のみに限定する必要はあるし、アクセスが拒否されたパターンの処理もする必要がある。

実装としては最初にPHPhotoLibraryを使ってPickerを初期化する。callbackを受け取ったらassetIdentifierを取得し、PhotoKitを使ってassetにアクセスできるようにする。
![スクリーンショット 2020-07-02 16.26.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100736/ad28c193-1dd0-b03b-d577-705dc77fc122.png)

# Limited Photos Library

iOS 14ではLimited Photos Libraryという新しい機能が使える。もしPhotoKitを使っていなければ特に心配する必要はない。Photo Libraryへのアクセスを求めるアプリでは、既存の選択肢に加え、Photos Libraryの一部のPHAssetsアクセスが許される選択肢が増える。
Limited Phots Libraryを使う場合、注意しなければいけないことがある。ピッカーはPhotos Libraryの全体を表示し、ユーザは全ての写真、動画を選択できるが、ピッカーで何を選択してもアクセスできるPHAssetsは変わらない。より詳しい情報は「Handle the Limited Photos Library in Your App」セッションを参照する。

# deprecatedなAPI

AssetsLibraryは数年前からdeprecatedになっているので、PhotoKitに乗り換えを。
新しい発表としては、UIImagePickerControllerの写真ライブラリ部分もdeprecatedになる。代わりにPHPickerViewControllerを採用することをおすすめする。

# まとめ

* 自分で写真動画を選択するピッカーを作るのではなく、PHPickerを採用することをおすすめする
* PHPickerは複数選択ができる
* PHPickerだと写真アプリと同じで一貫したUIを提供できるので、ユーザは操作方法がわかっている
* ピッカーを表示する前に写真ライブラリへのアクセスを要求しない
* アプリがPhotoKitを利用して写真ライブラリにアクセスしている場合、今一度ライブラリへのアクセスが必要なのか考えたり、代わりにPHPickerを使うようにする