【Swift/PhotoKit】PHAssetとは?モデルオブジェクトの取得と操作方法!

【Swift/PhotoKit】PHAssetとは?モデルオブジェクトの取得と操作方法!

この記事からわかること

  • PhotoKitとは?
  • モデルオブジェクト取得操作方法
  • PHPhotoLibraryクラスとは?
  • PHAsset使い方
  • アセットを取得(フェッチ)する方法
  • PHFetchResultクラスからオブジェクトを取り出すenumerateObjectsメソッドの使い方
  • 画像(UIImage)を取得する方法

index

[open]

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

みんなの誕生日

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

posted withアプリーチ

環境

参考文献:Apple-PhotoKit
参考文献:Fetching Objects and Requesting Changes

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の機能も使用できるようになります。

今回はPHAssetなどのモデルオブジェクトに焦点を当ててまとめていきます。

モデルオブジェクト

PhotoKitではフォトライブラリ内のデータを操作するためのAPIとしてモデルオブジェクトが複数用意されています。写真アプリ内のデータ構造をモデルオブジェクトにコピーして操作していくイメージです。

上記のようにPhotoKitでは写真アプリ内の画像や動画などはPHAssetオブジェクトとして、アルバムはPHAssetCollectionオブジェクトとして操作できるようになっています。

またこれらのインスタンスは読み取り専用のimmutable(不変)で、メタデータのみを含み、モデルオブジェクトに対してクエリを構築し情報をフェッチします。

オブジェクトを変更したい場合は変更要求オブジェクト(performChangesメソッド)を構築して、明示的に共有オブジェクト(PHPhotoLibrary)にコミットする必要があります。

おすすめ記事:【Swift/PhotoKit】デバイスに写真を保存・削除・更新する方法!

PHPhotoLibraryクラス

公式リファレンス:PHPhotoLibrary

PHPhotoLibraryクラスはユーザーの写真ライブラリへのアクセスと変更を管理するオブジェクトです。sharedメソッドから共有されるシングルトンのインスタンスを参照します。

class PHPhotoLibrary : NSObject

参照はシングルトンのインスタンスから

PHPhotoLibrary.shared()

役割

  1. 写真アプリへのアクセス許可をユーザーに申請する
  2. アセットとコレクションに変更する
  3. ライブラリの更新を検知する
  4. 更新メッセージの登録

PHAssetクラス

公式リファレンス:PHAsset

PHAssetクラスはフォトライブラリ内の画像や動画、Live Photoなどのモデルオブジェクトでした。保持しているプロパティは以下の通りです。

 class PHAsset : PHObject {
    var mediaType: PHAssetMediaType // ビデオやオーディオなどのアセットのタイプ
    var mediaSubtypes: PHAssetMediaSubtype // アセットのサブタイプ パノラマ写真や高フレームレートのビデオなど、特別な種類のアセットを識別
    var sourceType: PHAssetSourceType // アセットがフォトライブラリに入る手段
    var pixelWidth: Int // アセットデータの幅 
    var pixelHeight: Int // アセットデータの高さ 
    var creationDate: Date? // アセットの作成日時。
    var modifiedDate: Date? // アセットが最後に変更された日時
    var location: CLLocation? // アセットの位置情報。
    var duration: TimeInterval // 動画アセットの長さ (秒単位)
    var isFavorite: Bool // お気に入り
    var isHidden: Bool // 非表示
    var hasAdjustments: Bool // アセットに調整データが含まれているかどうか
    var adjustmentFormatIdentifier: NSString? // 調整フォーマットの識別子
}

このPHAssetを取得する方法を見ていきます。

アセットを取得(フェッチ)する方法

アセットを取得(フェッチ)するにはPHAssetの持つfetchAssetsメソッドを使用します。同名の引数違いで様々なメソッドが定義されていますが返り値がPHFetchResult<PHAsset>型になるのは共通です。

例えば指定したメディアタイプ(画像や動画など)のみのアセットを取得する場合は以下のようになります。

let fetchedImageResult:PHFetchResult = PHAsset.fetchAssets(with: .image , options: nil)

アセットコレクション内のアセットを取得

let fetchedCollectionResult:PHFetchResult = PHAsset.fetchAssets(in: assetCollection , options: nil)

識別子にマッチするアセットを取得

let fetchedIdentifierResult:PHFetchResult = PHAsset.fetchAssets(withLocalIdentifiers: assetIds, options: nil)

またフェッチにはPHFetchOptions型のオプションを渡すことができ順序のソートやフェッチする最大数を指定できます。

公式リファレンス:PHAsset

PHFetchResultクラス

PHFetchResultクラスはフェッチ結果を提供するクラスです。配列の要素にフェッチしたPHAsset、PHCollection,、PHAssetCollection、PHCollectionListのいずれかのオブジェクトを保持します。

class PHFetchResult<ObjectType> : NSObject where ObjectType : AnyObject

保持する個数に参照できるcountプロパティや存在するかどうかをチェックするcontainsメソッド、最初のオブジェクトを参照するfirstObjectプロパティなどが用意されています。

今回は保持しているオブジェクトを全てに特定のクロージャを実行できるenumerateObjectsを使用します。

公式リファレンス:enumerateObjects

PHFetchResult.enumerateObjects { (asset, index , pointer ) in
  // 
}

クロージャーの中では対象のオブジェクト、インデックス、取り出しを停止させるBool値の3つの引数を取得できます。

画像(UIImage)を取得する方法

ここで一度PHAssetを使ってフォトライブラリの画像を取得しビューとして表示させる流れをみていきます。

  1. info.plistにキーを追加
  2. UIの構築
  3. PHAssetのフェッチ
  4. 画像(UIImage)の取得

info.plistにキーを追加

PhotoKitを使用してアセットやコレクションの取得、ライブラリの更新など、アプリがPhotoKitの高度な機能を使用するためにはアプリ内からデバイスの写真アプリにアクセスできるように「info.plist」に「NSPhotoLibraryUsageDescription」キーを追加する必要があります。

【Swift UIKit】画像をカメラロールから保存/取得/削除する方法!

UIの構築

続いて必要となるUIを準備します。ViewControllerクラスには表示用のUIImageViewフォトライブラリのアセットを保持する用のプロパティを定義しておきます。またアセットを取得してセットするボタンと実際にビューに反映させるボタンも配置しておきます。

import UIKit
import PhotosUI

class PhotoKitViewController: UIViewController {
    
    let imageView = UIImageView() // 表示用View
    
    var photoAssets: [PHAsset] = [] // フォトライブラリ保持用
    
    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("Assetsを取得", for: .normal)
        button.addTarget(self, action: #selector(setAssets), for: .touchUpInside)
        view.addSubview(button)
        
        let button2 = UIButton()
        button2.backgroundColor = .brown
        button2.frame = CGRect(x: UIScreen.main.bounds.width/3, y: 300, width: UIScreen.main.bounds.width/3, height: 50)
        button2.setTitle("画像を取得", for: .normal)
        button2.addTarget(self, action: #selector(showImage), for: .touchUpInside)
        view.addSubview(button2)
        
        imageView.frame =  CGRect(x: 0, y: 400, width: UIScreen.main.bounds.width, height: 300)
        view.addSubview(imageView)
    }
    
    @objc  func setAssets() {
      //
    }
    
    @objc  func showImage(){
      //
    }
}

PHAssetのフェッチ

まずは現在のフォトライブラリをPhotoKitで操作できる形(アセット)になるようにPHAsset.fetchAssetsメソッドを使ってフェッチします。今回はオプションは指定なしにしておきました。またこのメソッドを呼び出した際に許可申請アラートが表示されます。取得できるのはPHFetchResult型のデータです。

@objc  func setAssets() {
//  let fetchOptions = PHFetchOptions()
    let assets: PHFetchResult = PHAsset.fetchAssets(with: .image, options: nil)
    assets.enumerateObjects { (asset, _ , _) in
        self.photoAssets += [asset]
    }
}

取得したPHFetchResultからをenumerateObjectsメソッドを使用してphotoAssetsプロパティに1つずつオブジェクト(PHAsset)を格納していきます。

画像(UIImage)の取得

PHAssetが取得できればPHImageManagerを使用してUIImageを取得するだけです。PHImageManagerの使用方法やリクエストオプションの概要は以下の記事を参考にしてください。

おすすめ記事:【Swift/PhotoKit】PHImageManagerの使い方!アセット操作と画像の取得

@objc  func showImage(){
    let manager: PHImageManager = PHImageManager()
    guard let firstAsset = photoAssets.first else { 
      return 
    }

    let requestOptions = PHImageRequestOptions()
    requestOptions.version = PHImageRequestOptionsVersion.current
    requestOptions.deliveryMode = PHImageRequestOptionsDeliveryMode.highQualityFormat
    requestOptions.resizeMode = PHImageRequestOptionsResizeMode.exact
    requestOptions.isSynchronous = true
    requestOptions.isNetworkAccessAllowed = true
    
    manager.requestImage(for: firstAsset,
        targetSize: imageView.frame.size,
        contentMode: PHImageContentMode.aspectFill,
        options: requestOptions) { (image, info) in
        DispatchQueue.main.async {
            self.imageView.image = image
            }
        }
}

カメラロールから取得した画像のメタデータを取得する方法

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

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

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

searchbox

スポンサー

ProFile

ame

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

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

New Article

index