PHPickerViewControllerで同期的に処理をする

こんにちは。この記事はコネヒトアドベントカレンダーの2日目の記事になります。前日の記事はこちらになります。よければご覧ください。

さて、みなさんiOSのPHPickerViewControllerを使っていますか? iOS14からPHPickerViewControllerが登場し、ライブラリから画像を取得する際の新しい選択肢になりました。いろんな特徴はありますが、ユーザー許可なしで画像を取得できるというのがあります。このおかげで画像取得のハードルが下がるので新機能を実装する際にはこちらを使うのが良いのかなと思います。

ただ、複数枚画像を選択した場合にはUIImageに変換する過程で選択順が非同期なので選択順が維持されません。今回実装したUIだと選択順を維持したかったので同期的に処理するようにしました。今回は、それについてお話をしたいと思います。

iOS 13から出たTaskという機能が使えるかなと思いますが、今回は dispatchSemaphoreを使い後続のUIImageを生成したら次のUIImageを生成するようにしました。dispatchSemaphoreの使い方はこちらのブログ記事が参考にさせていただきました。

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

        dismiss(animated: true)
        var images: Array<UIImage> = []
                
        let dispatchGroup = DispatchGroup()
        // 画像変換タスク名
        let dispatchQueue = DispatchQueue(label: "add_images")
        let dispatchSemaphore = DispatchSemaphore(value: 0)
        
        // 一枚ずつ画像を取得する
        for result in results {            
            let itemProvider = result.itemProvider
            if itemProvider.canLoadObject(ofClass: UIImage.self) {
                dispatchQueue.async(group: dispatchGroup) {
                    itemProvider.loadObject(ofClass: UIImage.self) { image, error in
                        
                        if let image = image as? UIImage {
                            images.append(image)
                            // カウントをインクリメント
                            dispatchSemaphore.signal()
                        } else {
                            print("error:  \(error.debugDescription)")
                        }

                    }
                    // カウントをデクリメント
                    dispatchSemaphore.wait()
                }
            }
        }
    }
}


最後の箇所で全ての処理が終わった際の処理を実行しています。これで非同期に生成されるUIImageで選択順を維持しつつ、次の処理を行えるようになりました。ということでdispatchSemaphoreを使うことで画像の選択順を維持しつつUIImageを取得することができました。めでたしめでたし。

次回はPMMとして活躍されているいぐちさんの記事になります。投稿楽しみにしております。