WWDC Quick Look 💓 By SwiftGGTeam
Improve access to Photos in your app

Improve access to Photos in your app

元の動画を見る

ハイライト

iOS 15 では、PHPicker に 3 つの大きな改善が加えられました。順序付けられた選択、事前に選択された写真、読み込みの進行状況です。また、クロスデバイスの写真同期を可能にする PHCloudIdentifier が追加され、制限付き写真ライブラリ モードでのカスタム アルバムの制限が緩和されます。

主な内容

アプリでは、ユーザーが複数の写真を選択できるようにする必要があります。以前のアプローチは、写真ライブラリの完全な権限を要求してから、自分で写真ブラウザを作成することでした。これは iOS 14 の PHPicker によって変更されました。システムはセレクターを提供し、アプリはユーザーが選択した写真のみを取得でき、プライバシーは保護されます。

iOS 15 はこれに基づいて 4 つのことを実行します。

まず第一にプライバシーの透明性です。(01:15) ユーザーは、設定に「システム フォト ピッカーのみを使用する」という指示が表示され、アプリが選択された写真のみにアクセスできることがわかります。これにより信頼性が高まり、開発者はカスタム実装ではなくシステム セレクターを使用するようになります。

2つ目は順序付き選択です。(02:02) ユーザーが複数の写真を選択する場合、順序を制御できます。セレクターには、単純なチェック マークの代わりに、数字 1、2、および 3 が表示されます。パズル、スライドショー、ソーシャルメディアの複数画像投稿などに便利です。

3 つ目は、写真を事前に選択することです。(02:45) ユーザーは前に 3 枚の写真を選択しました。セレクターを再度開くと、これら 3 枚の写真がデフォルトで選択されます。ユーザーは選択を解除したり、新しい写真を追加したりできます。これにより「再選択時に前に何を選択していたのか分からない」という悩みが解消されます。

4 番目は読み込みの進行状況です。(06:29) ユーザーが iCloud 写真最適化ストレージをオンにしている場合、写真はローカルではない可能性があります。以前は円しか表示できませんでしたが、NSProgress を通じて実際のダウンロードの進行状況を取得できるようになり、ユーザーに明確なフィードバックを提供できるようになりました。

写真ライブラリとの緊密な統合を必要とするアプリのために、iOS 15 には PHCloudIdentifier も導入されています。(08:58) ユーザーは iPhone で一連の写真を編集しました。iPad バージョンのアプリを開いたときに写真が自動的に表示されるはずです。問題は、ローカル識別子がデバイスごとに異なることです。PHCloudIdentifier は、iCloud 同期の詳細を操作することなく、デバイス間で同じ写真を検索するメカニズムを提供します。

最後に、制限付きフォト ギャラリー モードが改善されました。(15:10) iOS 14 の制限モードでは、アプリはカスタム フォト アルバムを作成したり、カスタム フォト アルバムにアクセスしたりできません。 iOS 15 ではこの制限が解除されます。アプリは独自のフォト アルバムを作成し、制限モードで通常どおり読み書きできます。こちらも追加presentLimitedLibraryPickerAPI はセレクターをアクティブにポップアップして、ユーザーがさらに写真を追加したり、完了ハンドラーを通じて新しい写真 ID を取得したりできるようにします。

##詳細

順序付けられた選択範囲と事前に選択された写真を構成する

import PhotosUI

var existingSelection: [PHPickerResult] = []

func presentPicker() {
    var config = PHPickerConfiguration(photoLibrary: .shared())
    config.selectionLimit = 0  // 0 は無制限を表す
    config.selection = .ordered  // 順序付き選択

    // 以前に選択済みの写真を事前選択
    let previousIdentifiers = existingSelection.compactMap { $0.assetIdentifier }
    config.preselectedAssetIdentifiers = previousIdentifiers

    let picker = PHPickerViewController(configuration: config)
    picker.delegate = self
    present(picker, animated: true)
}

キーポイント:

  • selection = .ordered順序付き選択を有効にすると、セレクターに数値シーケンスが表示されます。
  • preselectedAssetIdentifiers以前に選択した資産識別子の配列を渡します
  • 設定が必要ですPHPickerConfiguration(photoLibrary:)資産識別子を取得するには初期化が必要です。
  • デフォルトselection = .defaultチェックマークのみが表示され、順序は表示されません

選択結果を処理し、古いデータと新しいデータをマージします

extension ViewController: PHPickerViewControllerDelegate {
    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        picker.dismiss(animated: true)

        var updatedSelection: [PHPickerResult] = []

        for result in results {
            if let existing = existingSelection.first(where: {
                $0.assetIdentifier == result.assetIdentifier
            }) {
                // 事前選択され、解除されていないもの:古い結果(item provider を含む)を使用
                updatedSelection.append(existing)
            } else {
                // 新しく選択されたもの:新しい結果を使用
                updatedSelection.append(result)
            }
        }

        existingSelection = updatedSelection

        // 画像データを読み込む
        for result in updatedSelection {
            result.itemProvider.loadObject(ofClass: UIImage.self) { image, error in
                // 画像を処理
            }
        }
    }
}

キーポイント:

  • 事前に選択された写真の返された結果では、itemProvider転送する実際のデータがないため空です
  • 事前に選択された結果の空のアイテムプロバイダーを古いピッカー結果に置き換える必要があります
  • 選択を解除した写真は結果に表示されず、自然に除外されます
  • ユーザーが「キャンセル」をクリックすると、結果には事前に選択された資産識別子のみが含まれ、すべての項目プロバイダーは空になります。

読み込みの進行状況を表示

for result in results {
    let progress = result.itemProvider.loadObject(ofClass: UIImage.self) { image, error in
        // 読み込み完了
    }

    // NSProgress で UI を更新
    progress.observeValue(forKeyPath: "fractionCompleted", options: .new) { _, _, _ in
        DispatchQueue.main.async {
            self.progressView.progress = Float(progress.fractionCompleted)
        }
    }
}

キーポイント:

  • loadObject戻るNSProgressオブジェクト、ダウンロードの進行状況を観察できます
  • 写真が iCloud に保存されており、ローカルにダウンロードされていない場合、進行状況は 0 から始まり、徐々に増加します。
  • ローカルに存在する写真はすぐに完成し、進行状況は 1.0 に直接ジャンプします。

PHCloudIdentifier を使用してデバイス間で写真を同期する

// ソースデバイス:cloud identifier を取得
let localIdentifiers = selectedAssets.map { $0.localIdentifier }

PHPhotoLibrary.shared().cloudIdentifierMappings(forLocalIdentifiers: localIdentifiers) { mappings in
    var cloudIDs: [PHCloudIdentifier] = []

    for (localID, mapping) in mappings {
        if let cloudID = mapping.cloudIdentifier {
            cloudIDs.append(cloudID)
        } else if let error = mapping.error {
            print("Mapping failed for \(localID): \(error)")
        }
    }

    // cloudIDs をシリアライズして CloudKit 経由で同期
    let archivedData = try? NSKeyedArchiver.archivedData(withRootObject: cloudIDs, requiringSecureCoding: true)
    // CloudKit にアップロード...
}

キーポイント:

  • cloudIdentifierMappings(forLocalIdentifiers:)ローカル識別子からクラウド識別子へのマッピングを返します。
  • マッピングが失敗した可能性があるため、対処する必要がありますidentifierNotFoundそしてmultipleIdentifiersFound2種類の間違い
  • PHCloudIdentifier文字列にシリアル化し、ネットワークまたは CloudKit 経由で同期できます。
// ターゲットデバイス:cloud identifier でローカル写真を検索
let cloudIdentifiers: [PHCloudIdentifier] = // CloudKit からダウンロードしてデシリアライズ

PHPhotoLibrary.shared().localIdentifierMappings(forCloudIdentifiers: cloudIdentifiers) { mappings in
    var localIDs: [String] = []

    for (cloudID, mapping) in mappings {
        if let localID = mapping.localIdentifier {
            localIDs.append(localID)
        } else if let error = mapping.error {
            if let multipleIDs = error.userInfo[PHLocalIdentifiersErrorKey] as? [String] {
                // 複数一致した場合はユーザーに選択させる
            }
        }
    }

    let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: localIDs, options: nil)
    // 写真を表示
}

キーポイント:

  • localIdentifierMappings(forCloudIdentifiers:)ローカル識別子の逆引き
  • multipleIdentifiersFoundエラーが発生した場合、userInfo[PHLocalIdentifiersErrorKey]一致するすべてのローカル識別子が含まれます
  • マッピング操作はコストがかかるため、ロード時と保存時に実行することをお勧めします。ローカル識別子は日常のやり取りに使用されます。

制限付きフォト ライブラリ モードでフォト アルバムを管理する

// 制限モードでカスタムアルバムを作成
PHPhotoLibrary.shared().performChanges {
    let creationRequest = PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: "My App Photos")
    let placeholder = creationRequest.placeholderForCreatedAssetCollection
} completionHandler: { success, error in
    // 結果を処理
}

キーポイント:

  • iOS 15 以降、制限付きフォト ライブラリ モードでカスタム フォト アルバムを作成、取得、更新できるようになりました
  • アプリで作成した写真は、制限付き選択セットに自動的に追加されます
  • 深い統合が必要なシナリオでも、ユーザーに完全なアクセス許可を付与するようガイドすることが推奨されます。

制限された写真ピッカーをアクティブにポップアップする

// ユーザーが制限付き選択セットにさらに写真を追加できるようにする
PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: self) { identifiers in
    // identifiers はユーザーが新しく追加した写真識別子
    let newAssets = PHAsset.fetchAssets(withLocalIdentifiers: identifiers, options: nil)
    // 新しく追加された写真を処理
}

キーポイント:

  • presentLimitedLibraryPickerシステムセレクターをアクティブにポップアップします
  • 完了ハンドラーは、選択されたすべての写真ではなく、ユーザーが追加した写真識別子を返します。
  • 「写真を追加」ボタンをクリックしたときの通話に適しています

重要ポイント

  1. ソーシャルアプリ上での複数の写真の組織的なリリース
  • 対処方法: 複数の写真を選択するときに、ユーザーが公開順序を制御できるようにします。 1枚目が表紙、以下がシリアルナンバー順に並んでいます。
  • 実行する価値がある理由:selection = .orderedこれは 1 行のコードで実現できます。ユーザーが写真を選択すると、シリアル番号 1、2、3 が直接表示されます。
  • 開始方法: 設定config.selection = .ordered、結果配列が読み取られる順序は、ユーザーが指定した順序です。
  1. クロスデバイスの写真日記の同期
  • 内容: iPhone でユーザーが作成したフォト ジャーナルは、iPad や Mac で開くと同じ写真を自動的にロードします。
  • 実行する価値がある理由:PHCloudIdentifieriCloud 同期の複雑さは解消され、識別子を保存して CloudKit に同期するだけで済みます。
  • 開始方法: を使用しますcloudIdentifierMappingsクラウド ID を取得し、ターゲット デバイスの CloudKit データベースに同期します。localIdentifierMappings反捜査
  1. クロスデバイスでの写真編集プロジェクトの継続
  • 内容: 写真編集アプリを使用すると、ユーザーは iPhone で編集を開始し、iPad で続行できます。
  • 実行する価値がある理由: 編集プロジェクトのメタデータ (フィルター パラメーター、クロップ ボックス) は小さく、CloudKit を通じて同期されます。写真自体はPHCloudIdentifierを通じて特定されます
  • 開始方法: 編集パラメータと写真のクラウド識別子を CloudKit に保存し、ターゲット デバイスがチェックバックした後、編集状態を復元します。
  1. 制限モードでのスマートなフォト アルバム管理
  • 対処方法: 制限付きフォト ライブラリ モードでは、アプリは自動的にフォト アルバムを作成し、独自に生成したコンテンツ (コラージュ結果、フィルターされた写真など) を保存します。
  • 実行する価値がある理由: iOS 15 では、制限付きモードでのフォト アルバムの作成を妨げる制限が削除されます。ユーザーは通常、これを使用するために完全な権限を付与する必要はありません。
  • 開始方法: 現在の許可ステータスを検出し、制限モードで通常に呼び出しますcreationRequestForAssetCollection、作成後にユーザーの選択セットに自動的に追加されます
  1. 進行状況バーを使用した写真選択エクスペリエンス
  • 内容: iCloud 写真を選択すると、円を描く代わりに実際のダウンロードの進行状況が表示されます。
  • 実行する価値がある理由:loadObject返されましたNSProgressUIProgressView に直接バインドできるため、エクスペリエンスが大幅に向上します
  • 開始方法: モニタリングfractionCompletedプロパティ、進行状況バーの更新、読み込み完了後の非表示

関連セッション

コメント

GitHub Issues · utterances