【アプリ開発】画像の登録・サイズ変更(Swift/SwiftUI)

App

この記事ではSwitt/SwiftUIのアプリと写真アプリを連携させ、画像を載せる方法について記載します。
皆様のアプリ開発に役立てるように分かりやすく、実際のアプリの動き、コードから解説していきます。

完成画面

画像を挿入し、表示する画面になります。画面の動きを説明します。
– 画像挿入ボタンを押下
– 写真アプリが立ち上がる
– 画像を選択すると自動的に写真アプリが閉じる
– 画像挿入→画像挿入済みの変更
– プレビューボタンから画像を表示

解説

前提条件

今回紹介する画像の登録はアプリ開発をする目的を元にしています。なので画像はデータベース(CloudKit)に保存するための処理をプログラムに記載されています。
画像データはCloudKitに保存され、そのデータを画面上に表示させています。その際にただ画像として登録しているのではなく、Byte単位に分割してそれを保存→出力→そのByteデータを画像化しています。ただ「画像を画面上に出す」という処理よりは少し複雑になっています。

プログラム

以下が画像挿入の処理をする構造体を実行するSwiftUIのソースコード

import SwiftUI

struct ThreadPost: View {

    //画像のデータ情報を格納する
    @State var selectedImaged:Data = Data.init()
    //trueで構造体(ImagePicker())を実行する
    @State var showphoto = false
    var body: some View {
        //画像データが空っぽ、もしくは1000000Byte以上の場合※1000000Byte以上のデータ容量はCloudKitのレコードの保存できないため。
        if selectedImaged == Data.init() ||  selectedImaged.count > 1000000 {
            //画像挿入ボタンを押された時の処理
            Button(action: {
                showphoto = true
            }){
                Text("画像挿入")
             //モーダル表示する処理
            }.sheet(isPresented: $showphoto, onDismiss: {
                showphoto = false
            }){
                //構造体(ImagePicker())を実行
                ImagePicker(selectedImage: $selectedImaged, tex: $tex)
            }
          //画像挿入後も変更できるように同じ処理をする
        } else {
            Button(action: {
                showphoto = true
            }){
                Text("画像挿入済み")
            }.sheet(isPresented: $showphoto, onDismiss: {
                showphoto = false
            }){
                ImagePicker(selectedImage: $selectedImaged, tex: $tex)
            }
        }
    }
}

以下が写真アプリを起動させるSwiftUIのソースコード

import SwiftUI
import PhotosUI

struct ImagePicker: UIViewControllerRepresentable {
    //Coordinatorの作成の前にフォトライブラリーから取得した値を格納する変数。UIイメージ型(画像データを扱うための型)の変数。
    @Binding var selectedImage: Data
    @Binding var tex: String
    
    //戻り値にPHPickerViewControllerをとる
    func makeUIViewController(context: Context) -> PHPickerViewController {
        //インスタンス作成。PHPickerViewControllerのインスタンスを作ると時に設定の情報として渡す
        var conf = PHPickerConfiguration()
        //フォトライブラリーに表示されるアセットの種類を制限する(今回は画像のみ)
        conf.filter = .images
        //インスタンス作成。
        let picker = PHPickerViewController(configuration: conf)
        //picker(PHPickerViewController)の処理の一部をCoordinatorのインスタンスで行うこと指定する
        picker.delegate = context.coordinator
        return picker
    }
    //第1引数に使いたいUIKitViewControllerを指定。
    func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {
        //関数の中身はViewが更新されたときに行う処理を記述する。
    }
    //Coordinatorを使えるようにする記述
    func makeCoordinator() -> Coordinator {
        //self:コードが記述されているクラス、または構造体のことを指す(ImagePicker)
        return Coordinator(parent: self)
    }
    class Coordinator: PHPickerViewControllerDelegate {
        //Coordinatorの中でImagePickerを使えるようにするために記載
        //ImagePicker型の変数を作成
        let parent: ImagePicker
        //Coordinatorクラスが初期化され、parentプロパティにImagePickerの参照を設定
        init(parent: ImagePicker){
            self.parent = parent
        }
        //PHPickerViewControllerDelegateで指定された通りに記述。resultに選択された画像を配列の形で格納。引数ラベルにdidFinishPicking。つまりdidFinishPickingで使われる。
        func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
            picker.dismiss(animated: true)
            
            for image in results {
                //このままでは余計な部分があって使えない。なのでimage.itemProvider.loadObjectで必要な部分を取得。errorはNSItemProviderReadingのオプショナル型。なのでcompletionHandlerではUIImage型に変換した後にアンラップする
                image.itemProvider.loadObject(ofClass: UIImage.self, completionHandler: {(image, error) in
                    //imageがUIImage型へダウンキャストできるかどうかを判定。不正なダウンキャストによる実行時エラーを避ける条件分岐
                    if let image = image as? UIImage {
                        var data = image.jpegData(compressionQuality: 0.7)
                        if data!.count > 1000000 {
                            data = image.jpegData(compressionQuality: 0.6)
                        }
                        if data!.count > 1000000 {
                            data = image.jpegData(compressionQuality: 0.4)
                        }
                        if data!.count > 1000000 {
                            data = image.jpegData(compressionQuality: 0.3)
                        }
                        if data!.count > 1000000 {
                            data = image.jpegData(compressionQuality: 0.2)
                        }
                        if data!.count > 1000000 {
                            data = image.jpegData(compressionQuality: 0.0)
                        }
                        self.parent.selectedImage = data!
                        if data!.count > 1000000 {
                            self.parent.tex = "画像データが大きすぎます"
                        } else {
                            self.parent.tex = ""
                        }
                    }
                })
            }
        }
    }
}

data = image.jpegData(compressionQuality: 数値):imageのデータサイズを数値で掛け算を行い縮小させる。(1000000Byte以上のデータはCloudKitのレコードへデータ保存できないため。)

実行される関数の順番と説明

  1. makeCoordinatorメソッドを呼ぶ
    • Coordinatorクラスのインスタンスが作成され、ImagePickerインスタンスへの参照が渡される。
  2. Coordinatorクラスのイニシャライザを呼ぶ
    • Coordinatorクラスが初期化され、parentプロパティにImagePickerの参照が設定される。
  3. makeUIViewControllerメソッドを呼ぶ
    • PHPickerViewControllerのインスタンスが作成され、構成が設定される。
    • PHPickerViewControllerのデリゲートがCoordinatorに設定される。
  4. updateUIViewControllerメソッドを呼ぶ
    • このサンプルでは何も行っていない。
  5. ユーザーが画像を選択すると、picker(_:didFinishPicking:)メソッドが呼ばれる
    • 選択された画像の情報はPHPickerResultの配列として渡される。
    • 各画像に対して、itemProviderを使用して画像データを非同期で取得する。
    • 取得した画像データを条件に従って圧縮し、必要に応じて再圧縮する。

以上がアプリ上に画像を挿入するソースコードになります。
ImagePickerの処理は複雑で分かりにくいと思います。細かく解説しましたが、完璧に理解出来ずともサンプリとして利用いただき、トライアンドエラーで少しずつ理解していくのがお勧めです。

アプリ”NowShare”をダウンロード
情報共有アプリ"NowShare"
今からみんなでナレッジ共有! 本当に欲しい情報をキャッチ&リリース 教えてあげたい情報を知りたい人にすぐに共有。 ためになった情報にはいいねを押して盛り上げよう!
Appエンジニア
ナスジニアをフォローする
🍆ナスジニアのブログ(iPhoneアプリ開発)

コメント

タイトルとURLをコピーしました