Swift - Photo Library から複数の画像を選択する方法
ここでは、Swift で PHPickerViewController を使って、Photo Library から複数の画像を選択する方法をご説明します。
テスト用の iOS アプリの作成する
まずはテスト用に Photo Library から複数の画像を選択する iOS アプリを作ります。
新規アプリの作り方、ボタンの追加の方法、アクションの作り方などがわからない方は「基本的な iOS アプリの作り方」をご覧ください。
Xcode で [iOS] の [App] の新規プロジェクトを作成します。
デザインや場所などは適当で良いので、Main ストーリーボードの View Controller に、ファイル名を表示する用のラベルを三つ、選択した画像を表示するようの Image View を三つ、ボタン (Select from Photo Library) をひとつ追加します。
Label から fileNameLabel1、fileNameLabel2、fileNameLabel3、Image View から imageView1、imageView2、imageView3 という名前でアウトレットを作っておきます。
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var fileNameLabel1: UILabel!
@IBOutlet weak var fileNameLabel2: UILabel!
@IBOutlet weak var fileNameLabel3: UILabel!
@IBOutlet weak var imageView1: UIImageView!
@IBOutlet weak var imageView2: UIImageView!
@IBOutlet weak var imageView3: UIImageView!
override func viewDidLoad() {
}
}
Select from Photo Library ボタンから selectTapped という名前で touchUpInside のアクションを作っておきます。
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var fileNameLabel1: UILabel!
@IBOutlet weak var fileNameLabel2: UILabel!
@IBOutlet weak var fileNameLabel3: UILabel!
@IBOutlet weak var imageView1: UIImageView!
@IBOutlet weak var imageView2: UIImageView!
@IBOutlet weak var imageView3: UIImageView!
override func viewDidLoad() {
}
@IBAction func selectTapped(_ sender: Any) {
}
}
ImageFile.swift という名前のファイルを追加して、以下のような ImageFile という名前の struct を定義しておきます。
選択されたファイルの情報を保持するのに使います。
import Foundation
import UIKit
struct ImageFile {
var FileName: String?
var ImageData: UIImage?
}
Swift で Photo Library から複数の画像を選択する方法
それでは、Swift で Photo Library から複数の画像を選択するコードを書いていきましょう。
ここでは、PHPickerViewController を使います。
PHPickerViewController は Photo Library にある画像を選択する UI と機能を提供してくれる view controller です。
ViewController.swift を以下のように変更します。
import UIKit
import PhotosUI
class ViewController: UIViewController, PHPickerViewControllerDelegate {
@IBOutlet weak var fileNameLabel1: UILabel!
@IBOutlet weak var fileNameLabel2: UILabel!
@IBOutlet weak var fileNameLabel3: UILabel!
@IBOutlet weak var imageView1: UIImageView!
@IBOutlet weak var imageView2: UIImageView!
@IBOutlet weak var imageView3: UIImageView!
var phPicker: PHPickerViewController!
override func viewDidLoad() {
super.viewDidLoad()
var config = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
config.filter = .images
config.selectionLimit = 3
phPicker = PHPickerViewController(configuration: config)
phPicker.delegate = self
resetFiles()
}
func resetFiles() {
fileNameLabel1.text = ""
fileNameLabel2.text = ""
fileNameLabel3.text = ""
imageView1.image = nil
imageView2.image = nil
imageView3.image = nil
}
@IBAction func selectTapped(_ sender: Any) {
present(phPicker, animated: true)
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
if results.isEmpty {
return
}
var selectedOrder = [String]()
var dictImages = [String:ImageFile]()
let group = DispatchGroup()
for result in results {
let id = result.assetIdentifier ?? ""
selectedOrder.append(id)
group.enter()
let provider = result.itemProvider
if provider.canLoadObject(ofClass: UIImage.self) {
provider.loadObject(ofClass: UIImage.self) { image, _ in
if let img = image as? UIImage {
dictImages[id] = ImageFile(FileName: provider.suggestedName, ImageData: img)
}
group.leave()
}
} else {
group.leave()
}
}
group.notify(queue: .main) {
var images = [ImageFile]()
for id in selectedOrder {
if dictImages[id] != nil {
images.append(dictImages[id]!)
}
}
self.displayFiles(images: images)
}
}
func displayFiles(images: [ImageFile]) {
resetFiles()
for (i, image) in images.enumerated() {
if i == 0 {
fileNameLabel1.text = image.FileName
imageView1.image = image.ImageData
} else if i == 1 {
fileNameLabel2.text = image.FileName
imageView2.image = image.ImageData
} else if i == 2 {
fileNameLabel3.text = image.FileName
imageView3.image = image.ImageData
}
}
}
}
コードを順を追って説明します。
まず、PHPickerViewController を使うので、2 行目で、PhotosUI を import して、 4 行目に PHPickerViewControllerDelegate を追加しています。
13 行目で phPicker という名前で PHPickerViewController を定義しています。
viewDidLoad() の 18 ~ 23 行目で、PHPickerViewController の設定をしています。
PHPickerConfiguration を定義して、filter では images 画像ファイルのみを選択するように指定し、selectionLimit で選択できる最大の数を 3 に設定しています。
selectionLimit の default は 1 で、最大数を設定したくない場合は 0 にします。
この config を指定して、phPicker を生成し、画像が選択された時に picker(_:didFinishPicking:) が呼ばれるように delegate に self を指定しています。
resetFiles() は、label と Image View をクリアしているだけです。
37 ~ 39 行目の selectTapped() で、present(phPicker, animated: true) で、ボタンがタップされた時に画像の選択画面が表示されるようにしています。
画像選択画面で、Add や Cancel ボタンがタップされた時に picker(_:didFinishPicking:) が呼ばれます。
42 行目で、選択画面を close しています。
results に選択されたアセットが入ってきますが、Cancel がタップされた場合は要素数が 0 になるので、return しています。
今回は選択された順番を保持するようなコードにしています。
results の中のアセットをループして、itemProvider の loadObject で画像を抽出しています。
loadObject の completionHandler が呼ばれる順番が、画像が選択された順番になるとは限りません。
そこで、選択順を保持するために、DispatchGroup を使っています。
enter() が呼ばれた後、全ての leave() が呼ばれてから、notify() が実行されます。
選択されたアセットの id を順番に selectedOrder に保存しておいて、notify() で仮に dictImages 保存しておいたファイル名を画像データを選択順になるように配列を生成しています。
選択された順番を保持する必要がない場合は、id の順番を保持したり、DispatchGroup をつかわずに、loadObject() を実行して画像を抽出していくだけでよいと思います。
displayFiles() では、選択順に入っている ImageFile の配列のデータを Label と Image View に表示しています。
以上が、Swift で Photo Library から複数の画像を選択するコードです。
iOS アプリをシミュレーターでテストする
それでは、作った iOS アプリをビルドして実行してみましょう。
ツールバー左側の ボタンをクリックすると、選択されているシミュレータが立ち上がり、アプリがインストールされて実行されます。
Select from Photo Library ボタンをタップします。
Photo Library の画像を選択する画面が表示されます。
写真を三つ選択して、Add ボタンをタップします。
選択画面が閉じ、メインの画面に選択した画像とファイル名が選択した順番に表示されます。
以上、Swift で PHPickerViewController を使って、Photo Library から複数の画像を選択する方法をご説明しました。