【Swift UIKit】画像をカメラロールに保存する方法!UIImageWriteToSavedPhotosAlbum

【Swift UIKit】画像をカメラロールに保存する方法!UIImageWriteToSavedPhotosAlbum

この記事からわかること

  • SwiftUIKit画像カメラロール(写真アプリのアルバム)に保存する方法
  • NSCameraUsageDescriptionキーの指定
  • UIImageWriteToSavedPhotosAlbum使い方

index

[open]

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

みんなの誕生日

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

posted withアプリーチ

環境

この記事では画像(UIImage)をカメラロール(写真アプリのアルバム)に保存する方法をまとめていきます。アプリ内からカメラを起動して写真を撮影する方法については以下の記事を参考にしてください。

おすすめ記事:【Swift UIKit】アプリ内からカメラで写真を撮影する方法!

画像(UIImage)をカメラロールに保存する方法

Swiftで画像(UIImage)をカメラロールに保存する方法は2種類あります。活用する場面によって適切な方を選択して使用することをおすすめします。今回はUIImageWriteToSavedPhotosAlbumメソッドの使い方をまとめていきます。

UIImageWriteToSavedPhotosAlbumメソッド

PhotoKitのPHPhotoLibrary

UIImageWriteToSavedPhotosAlbumメソッド

参考文献:公式リファレンス:UIImageWriteToSavedPhotosAlbum

UIImageWriteToSavedPhotosAlbumメソッドはどこからでも利用できるグローバル関数として定義されているUIImageををカメラロールに保存するためのメソッドです。

func UIImageWriteToSavedPhotosAlbum(
    _ image: UIImage,
    _ completionTarget: Any?,
    _ completionSelector: Selector?,
    _ contextInfo: UnsafeMutableRawPointer?
)

完了セレクターとして渡すメソッドは以下のシグネチャーに準拠する必要があります。


- (void)image:(UIImage *)image
    didFinishSavingWithError:(NSError *)error
                 contextInfo:(void *)contextInfo;

おすすめ記事:【Swift UIKit】#selectorとは?使い方と@objcとsenderの意味まとめ

画像(UIImage)をカメラロールに保存する実装手順

では実際に画像(UIImage)をカメラロールに保存する流れを実装していきたいと思います。

実装の流れ

  1. 「info.plist」にNSPhotoLibraryUsageDescriptionキーを追加
  2. アプリ内から写真を撮影する機能を実装
  3. 撮影した写真をUIImageViewに保存して表示
  4. UIImageViewに保存されている画像データをカメラロールに保存する

info.plistにNSPhotoLibraryUsageDescriptionキーを追加

アプリ内からデバイスのカメラロールにアクセスするためには「info.plist」にNSPhotoLibraryUsageDescriptionキーを追加する必要があります。

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

「info.plist」を開いたらKeyにNSPhotoLibraryUsageDescriptionと入力し、Valueにはカメラロールを使用する旨を記載しておきます。自動でPrivacy - Photo Library Usage Descriptionに変換されます。

今回はカメラも使用するのでデバイスのカメラにアクセスできるようにNSCameraUsageDescriptionキーも追加しておきます。

【Swift UIKit】アプリ内からカメラで写真を撮影する方法!

アプリ内から写真を撮影する機能を実装

アプリ内から写真を撮影する機能の実装方法はコードのみ記述しておきます。こちらの記事を参考にしてください。

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:)メソッド

セレクターメソッドはシグネチャーに準じて定義していきます。errornilかどうかで成功に可否を識別できます。

@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枚保存するようにして結果を受け取ってから次の画像を保存するように実装してみましたが、時間がかかりすぎる上にたまに失敗してしまったので一度に大量の画像を保存することはできないのかもしれません。

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

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

searchbox

スポンサー

ProFile

ame

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

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

New Article

index