【Swift/PhotoKit】PHPickerViewControllerで画像を取得する方法!写真アプリの操作

この記事からわかること
- PhotoKitとは?
- PhotoKitの使用方法
- PHPickerViewControllerの使い方
- UIImagePickerControllerとの違い
- 写真アプリを操作する方法
- SwiftのUIKitで画像をカメラロールから選択するには?
index
[open]
\ アプリをリリースしました /
環境
- Xcode:15.0.1
- iOS:17.1
- Swift:5.9
- macOS:Sonoma 14.1
参考文献:Apple-PhotoKit
PhotoKitとは?
そもそもPhotoKitとはApple製のデバイスに入っている「写真アプリ」で管理されている写真や動画を操作するためのAPIを提供している技術です。実際の中身はPhotos FrameworkとPhotosUI Frameworkの2つのフレームワークに分けられて構成されており、それぞれをimportすることで使用できるようになります。
import Photos
import PhotosUI
Photos
カメラロール(フォトライブラリ)を管理するオブジェクトを提供するPHPhotoLibrary
や、実際の画像や動画などのアセットを管理するPHAsset
、アセットのコレクション(アルバムなど)を表現するPHAssetCollection
などといった基本的な写真アプリ操作機能を提供。
PhotosUI
画像のピッカービューを構築するためのPHPickerViewController
やその構成を定義するPHPickerConfiguration
などUIに関する機能を提供。
またPhotosUI
の中にPhotos
が含まれているためPhotosUI
のみでPhotos
の機能も使用できるようになります。
今回はPHPickerViewControllerに焦点を当ててまとめていきます。
おすすめ記事:【Swift/PhotoKit】画像ファイル名を指定して保存する方法!DCF規格とは?
UIImagePickerControllerとの違い
PHPickerViewControllerと同じく写真アプリのフォトライブラリから画像を選択するピッカービューの構築方法にはUIImagePickerController
を使用した方法もあります。しかしこちらでは写真の複数選択や検索などがサポートされておらずより写真アプリらしいユーザーインターフェースをPhotoKitでは実装できるようになっています。
さらにPHPickerViewController
ではItemProviderを介することでユーザーからの明示的な写真アプリへのアクセス許可を承認する必要とせずに選択できるのも大きなメリットの1つになっています。
UIImagePickerControllerの使用方法については以下の記事を参考にしてください。
おすすめ記事:【Swift UIKit】画像をカメラロールから選択/保存/取得/削除する方法!
画像を選択する:PHPickerViewController
参考文献:公式リファレンス:Selecting Photos and Videos in iOS
PHPickerViewController
を使用してフォトライブラリから画像を選択することができます。フォトライブラリから画像を選択するだけの場合ではユーザーが写真アプリへのアクセスを承認していなくても操作することが可能なようです。
実装手順
- ピッカーの構成(フィルタリングなど)を定義
- その構成を元にPHPickerViewControllerを構築
- PHPickerViewControllerDelegateに準拠
- 選択したItemProviderから画像を取得
例としてピッカーから選択した画像をアプリ内に表示させることを目標に作成していきます。まずはPhotoKitViewController
を作成し中に必要となるUIを記述していきます。
import UIKit
import PhotosUI
class PhotoKitViewController: UIViewController {
let imageView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton()
button.backgroundColor = .orange
button.frame = CGRect(x: UIScreen.main.bounds.width/3, y: 200, width: UIScreen.main.bounds.width/3, height: 50)
button.setTitle("画像を選択", for: .normal)
button.addTarget(self, action: #selector(showPhotoPicker), for: .touchUpInside)
view.addSubview(button)
imageView.frame = CGRect(x: 0, y: 400, width: UIScreen.main.bounds.width, height: 300)
view.addSubview(imageView)
}
@objc func showPhotoPicker() {
// ピッカーの構成(フィルタリングなど)を定義
// その構成を元にPHPickerViewControllerを構築
}
}
ここではボタンとそのアクション(まだ空の状態)、画像を表示させるためのUIImageView
を配置しておきます。
1-1.ピッカーの構成:PHPickerConfiguration
公式リファレンス:PHPickerConfiguration
表示するピッカーの構成を定義するにはPHPickerConfiguration
構造体を使用します。プロパティにはPHPickerFilter
型のフィルタリングとInt
型で選択可能な画像の枚数を指定できます。
struct PHPickerConfiguration {
var filter: PHPickerFilter? // フィルタリング
var selectionLimit: Int // 選択可能な数
}
1-2.フィルタリング:PHPickerFilter
PHPickerFilter
構造体ではフォトライブラリからピックアップできるアセットの種類が定数として、定数を組み合わせた条件の構築がメソッドとして定義されています。
struct PHPickerFilter {
// MARK: - constant
static let bursts: PHPickerFilter // 複数の高速写真を含むアセット
static let cinematicVideos: PHPickerFilter // 被写界深度が浅く、フォーカスの遷移が激しい動画
static let depthEffectPhotos: PHPickerFilter // 深度情報を持つ写真
static let images: PHPickerFilter // 画像 ( Live Photosを含む )
static let livePhotos: PHPickerFilter // Live Photos
static let panoramas: PHPickerFilter // パノラマ写真を
static let screenRecordings: PHPickerFilter // 画面記録
static let screenshots: PHPickerFilter // スクリーンショット
static let slomoVideos: PHPickerFilter // スローモーション動画
static let timelapseVideos: PHPickerFilter // タイムラプス
static let videos: PHPickerFilter // 動画アセット
// MARK: - method
static func all(of: [PHPickerFilter]) -> PHPickerFilter // 指定したフィルターのみを含むフィルターを作成
static func not(PHPickerFilter) -> PHPickerFilter // 指定したフィルターを除外するフィルターを作成
static func any(of: [PHPickerFilter]) -> PHPickerFilter // 配列内のフィルターを組み合わせたフィルターを作成
}
フィルタリングを何も指定しない場合は全てのアセットが選択可能になります。今回は画像のみかつ選択できる枚数を1枚に制限して作成してみます。
var configuration = PHPickerConfiguration()
configuration.filter = PHPickerFilter.images
// 例 configuration.filter = PHPickerFilter.any(of: [.livePhotos, .videos])
configuration.selectionLimit = 1
2-1.PHPickerViewControllerを構築
公式リファレンス:PHPickerViewController
次に先ほど定義した構成を元に実際に表示させるピッカービューを構築します。init(configuration: PHPickerConfiguration)
でインスタンス化し、delegateに自身をセット、present
メソッドでモーダル表示させます。
おすすめ記事:【Swift UIKit】モーダルウィンドウの実装方法!segueとpresentの2パターン
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
present(picker, animated: true, completion: nil)
2-2.PHPickerViewControllerDelegateに準拠
公式リファレンス:PHPickerViewControllerDelegate
またdelegateに自身をセットするためViewControllerクラスを拡張しPHPickerViewControllerDelegate
に準拠させます。
おすすめ記事:【Swift】delegate(デリゲート)とは?使い方とメリット
extension PhotoKitViewController: UINavigationControllerDelegate, PHPickerViewControllerDelegate {
//
}
ここまでで以下のようになっています。
import UIKit
import PhotosUI
class PhotoKitViewController: UIViewController {
let imageView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton()
button.backgroundColor = .orange
button.frame = CGRect(x: UIScreen.main.bounds.width/3, y: 200, width: UIScreen.main.bounds.width/3, height: 50)
button.setTitle("画像を選択", for: .normal)
button.addTarget(self, action: #selector(showPhotoPicker), for: .touchUpInside)
view.addSubview(button)
}
@objc func showPhotoPicker() {
var configuration = PHPickerConfiguration()
configuration.filter = PHPickerFilter.images
configuration.selectionLimit = 1
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
present(picker, animated: true, completion: nil)
}
}
extension PhotoKitViewController: UINavigationControllerDelegate, PHPickerViewControllerDelegate {
//
}
2-3.選択したItemProviderから画像を取得
PHPickerViewControllerDelegate
のpicker(PHPickerViewController, didFinishPicking: [PHPickerResult])
メソッドを使用してユーザーが選択を完了またはキャンセルしたことを受け取ります。
extension PhotoKitViewController: UINavigationControllerDelegate, PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
if let itemProvider = results.first?.itemProvider{
if itemProvider.canLoadObject(ofClass: UIImage.self) {
itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in
guard let image = image as? UIImage else {
return
}
DispatchQueue.main.async {
self?.imageView.image = image
}
}
}
}
self.dismiss(animated: true)
}
}
結果は配列形式のPHPickerResult形式で取得できます。PHPickerResult
のプロパティからは選択されたアセットを識別するID(assetIdentifier:String型
)とアセットを表示させるプロバイダー(itemProvider:NSItemProvider型
)に参照できます。このプロバイダーから対象のデータ(画像や動画など)を取得することが可能です。assetIdentifier
の場合は以下の記事を参考にしてください。
おすすめ記事:【Swift/PhotoKit】PHImageManagerの使い方!アセット操作と画像の取得
今回はUIImage(画像)として表示させたいのでitemProvider
プロパティを参照します。今回は1つしか選択されていないはずなのでfirst
を使って決め内で先頭を取り出し、選択したアセットのプロバイダーを取得します。
canLoadObject
メソッドは対象のプロバイダーオブジェクトを読み込めるかどうかを識別し真偽値で返します。問題なければloadObject
メソッドで対象の画像の取得を試みます。
loadObject
メソッドは非同期で実行されるメソッドでそのcompletionHandler
から対象のデータとエラーに参照できます。
おすすめ記事:【Swift】completionHandlerとは?使い方と@escapingの意味
またビューを更新するのでメインスレッドでimageView
プロパティにセットして表示させています。
最後にピッカービューを明示的に閉じる必要があるためdismiss
を記述します。これを書き忘れるとピッカーが閉じれないので注意してください。
おすすめ記事:【Swift】DispatchQueueの使い方!GCDで非同期と遅延処理
取得した画像のメタデータを取得する方法
PHPickerViewController
を使用してアルバムから写真を取得している場合に写真のメタデータ(撮影日時や撮影地など)を取得するにはいくつか注意点がありました。詳細は以下の記事を参考にしてください。
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。