【Swift UIKit】画像をカメラロールに保存する方法!UIImageWriteToSavedPhotosAlbum
この記事からわかること
- SwiftのUIKitで画像をカメラロール(写真アプリのアルバム)に保存する方法
- NSCameraUsageDescriptionキーの指定
- UIImageWriteToSavedPhotosAlbumの使い方
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
環境
- Xcode:15.0.1
- watchOS:10.0
- Swift:5.9
- macOS:Sonoma 14.1
この記事では画像(UIImage)をカメラロール(写真アプリのアルバム)に保存する方法をまとめていきます。アプリ内からカメラを起動して写真を撮影する方法については以下の記事を参考にしてください。
おすすめ記事:【Swift UIKit】アプリ内からカメラで写真を撮影する方法!
画像(UIImage)をカメラロールに保存する方法
Swiftで画像(UIImage)をカメラロールに保存する方法は2種類あります。活用する場面によって適切な方を選択して使用することをおすすめします。今回はUIImageWriteToSavedPhotosAlbum
メソッドの使い方をまとめていきます。
UIImageWriteToSavedPhotosAlbumメソッド
- グローバル関数として定義
- 1行のコードで画像を保存
- 完了ハンドラーが提供されていない
- 完了セレクターから結果を取得
- 写真ライブラリへのアクセス権限が必要
PhotoKitのPHPhotoLibrary
- iOS8以降で利用可能なフレームワーク
- 保存の進行状況を監視できる完了ハンドラーが提供
- アルバムへの保存やメタデータの変更など、より高度な操作が可能
- PHAuthorizationStatusを使用して許可の確認が可能
UIImageWriteToSavedPhotosAlbumメソッド
参考文献:公式リファレンス:UIImageWriteToSavedPhotosAlbum
UIImageWriteToSavedPhotosAlbum
メソッドはどこからでも利用できるグローバル関数として定義されているUIImageををカメラロールに保存するためのメソッドです。
func UIImageWriteToSavedPhotosAlbum(
_ image: UIImage,
_ completionTarget: Any?,
_ completionSelector: Selector?,
_ contextInfo: UnsafeMutableRawPointer?
)
- image:保存したい画像(UIImage)
- completionTarget:保存後にセレクターが呼び出されるオブジェクト
- completionSelector:保存後に呼び出すオブジェクトのメソッドセレクター
- contextInfo:セレクターに渡すコンテキスト固有のデータへのオプションのポインター
完了セレクターとして渡すメソッドは以下のシグネチャーに準拠する必要があります。
- (void)image:(UIImage *)image
didFinishSavingWithError:(NSError *)error
contextInfo:(void *)contextInfo;
おすすめ記事:【Swift UIKit】#selectorとは?使い方と@objcとsenderの意味まとめ
画像(UIImage)をカメラロールに保存する実装手順
では実際に画像(UIImage)をカメラロールに保存する流れを実装していきたいと思います。
実装の流れ
- 「info.plist」にNSPhotoLibraryUsageDescriptionキーを追加
- アプリ内から写真を撮影する機能を実装
- 撮影した写真をUIImageViewに保存して表示
- UIImageViewに保存されている画像データをカメラロールに保存する
info.plistにNSPhotoLibraryUsageDescriptionキーを追加
アプリ内からデバイスのカメラロールにアクセスするためには「info.plist」にNSPhotoLibraryUsageDescription
キーを追加する必要があります。
「info.plist」を開いたらKeyにNSPhotoLibraryUsageDescription
と入力し、Valueにはカメラロールを使用する旨を記載しておきます。自動でPrivacy - Photo Library Usage Description
に変換されます。
今回はカメラも使用するのでデバイスのカメラにアクセスできるようにNSCameraUsageDescription
キーも追加しておきます。
アプリ内から写真を撮影する機能を実装
アプリ内から写真を撮影する機能の実装方法はコードのみ記述しておきます。こちらの記事を参考にしてください。
import UIKit
class ViewController: UIViewController {
// MARK: - ImageView
@IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - 撮影ボタンアクション
@IBAction func takePictureTapped() {
if UIImagePickerController.isSourceTypeAvailable(.camera) {
let pickerView = UIImagePickerController()
pickerView.sourceType = .camera
pickerView.delegate = self
self.present(pickerView, animated: true)
}
}
}
extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let image = info[.originalImage] as! UIImage
imageView.image = image
self.dismiss(animated: true) // 選択画面を終了する
}
}
撮影した写真をUIImageViewに保存して表示
またViewController
を拡張してUIImagePickerControllerDelegate
に準拠させることでカメラで撮影した画像を取得し表示用のimageView
に格納しています。
UIImageViewに保存されている画像データをカメラロールに保存する
今回の本題であるカメラロールに保存する部分を実装していきます。Storyboardからボタンを追加し、紐付けるアクションを定義します。
まずは画像がちゃんと格納されているか(nilでないか)をguard〜else
構文で識別します。存在しない場合は処理を終了させます。問題なければ実際に画像を保存させるUIImageWriteToSavedPhotosAlbum
メソッドを呼び出して各引数に適切な値を渡していきます。
// MARK: - カメラロールに保存する
@IBAction func saveToPhotoLibraryTapped(_ sender: Any) {
guard let image = imageView.image else {
print("画像がないよ")
return
}
UIImageWriteToSavedPhotosAlbum(image, self, #selector(image(image:didFinishSavingWithError:contextInfo:)), nil)
}
image(image:didFinishSavingWithError:contextInfo:)メソッド
セレクターメソッドはシグネチャーに準じて定義していきます。error
がnil
かどうかで成功に可否を識別できます。
@objc func image(image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
if let error = error {
print("失敗:\(error)")
} else {
print("写真の保存に成功したよ")
}
}
これで全ての実装が完了しました。正常に動作するか実機にビルドして確認してみてください。
全体のコード
import UIKit
class ViewController: UIViewController {
// MARK: - ImageView
@IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: -
@IBAction func tapped(sender: UIButton) {
self.present(BarCodeReaderVC(), animated: true, completion: nil)
}
// MARK: - 撮影ボタンアクション
@IBAction func takePictureTapped() {
if UIImagePickerController.isSourceTypeAvailable(.camera) {
let pickerView = UIImagePickerController()
pickerView.sourceType = .camera
pickerView.delegate = self
self.present(pickerView, animated: true)
}
}
// MARK: - カメラロールに保存する
@IBAction func saveToPhotoLibraryTapped(_ sender: Any) {
guard let image = imageView.image else {
print("画像がないよ")
return
}
UIImageWriteToSavedPhotosAlbum(image, self, #selector(image(image:didFinishSavingWithError:contextInfo:)), nil)
}
@objc func image(image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
if let error = error {
print("失敗:\(error)")
} else {
print("写真の保存に成功したよ")
}
}
}
extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let image = info[.originalImage] as! UIImage // カメラで撮影した画像を取得
imageView.image = image
self.dismiss(animated: true) // 選択画面を終了する
}
}
連続で複数枚の写真を保存する
一度の処理で配列形式でUIImage
を渡し連続でカメラに保存する処理を実装してみましたが、途中でエラーが発生し失敗してしまいました。
func savePhotosToAlbum(_ images: [UIImage]) {
for image in images {
UIImageWriteToSavedPhotosAlbum(image, self, #selector(image(image:didFinishSavingWithError:contextInfo:)), nil)
}
}
上記の実装ではダメだったので1枚保存するようにして結果を受け取ってから次の画像を保存するように実装してみましたが、時間がかかりすぎる上にたまに失敗してしまったので一度に大量の画像を保存することはできないのかもしれません。
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。