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

この記事からわかること

  • PhotoKitとは?
  • PhotoKitの使用方法
  • PHPickerViewController使い方
  • UIImagePickerControllerとの違い
  • 写真アプリ操作する方法
  • SwiftUIKit画像カメラロールから選択するには?

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

参考文献: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を使用してフォトライブラリから画像を選択することができます。フォトライブラリから画像を選択するだけの場合ではユーザーが写真アプリへのアクセスを承認していなくても操作することが可能なようです。

実装手順

  1. ピッカーの構成(フィルタリングなど)を定義
  2. その構成を元にPHPickerViewControllerを構築
  3. PHPickerViewControllerDelegateに準拠
  4. 選択した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 // 配列内のフィルターを組み合わせたフィルターを作成
}

公式リファレンス: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から画像を取得

PHPickerViewControllerDelegatepicker(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を使用してアルバムから写真を取得している場合写真のメタデータ(撮影日時や撮影地など)を取得するにはいくつか注意点がありました。詳細は以下の記事を参考にしてください。

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index