システム開発<基本編>SwiftUIでQRコード生成アプリ

Swift

前回は、Xcode(IDE)プロジェクト作成を行う記事を紹介しました。

 

今回は、SwiftUIQRコードを生成するアプリケーションを開発する記事を

紹介しようと思います。

SwiftUIでQRコード生成アプリ

 

以前、PythonQRコード生成ツールを紹介しましたので、興味のある方は、

下記を参照してみて下さい。

 

 

習得内容

開発言語習得する場合、基本文法コンポーネントの使い方などを机上で学んでから、

開発を進める事が多い(王道?)と思いますが、こちらでは、実際にSwifUIを記述しながら、

習得して行きます。

 

今回、SwiftUIQRコードを生成するアプリケーションを生成する事で、下記の内容を

習得する事が出来ます。

  1. ContentView:メインメニュー(ランチャー)
  2. 3大UIボタン(Button)テキスト(Text)イメージ(Image)
  3. フルモーダル表示fullScreenCover()
  4. 状態管理(Property Wrapper)@State@Binding
  5. Core Image画像処理解析を行うフレームワーク
  6. QRコード生成CIFilterCIContextCGAffineTransform
  7. SF Symbolsシステムシンボル

 

動作環境

先ずは、アプリが動作する環境を紹介します。

  • mac OS:Sequoia 15.6.1
  • iOS:26.0
  • Xcode:26.0.1
  • Swift:6.2.1

 

ContentView:メインメニュー(ランチャー)

前回の記事では、Xcode(IDE)にて、SwiftMenu というプロジェクト作成をしましたので、

下記のプログラムが自動生成されていると思います。

  1. SwiftMenuApp:初期起動プログラム → ContentView を呼び出す
  2. ContentView:初期起動画面(Hello, world!) を表示します

 

自動生成された ContentView のコードは、下記になります。


import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Hello, world!")
        }
        .padding()
    }
}

#Preview {
    ContentView()
}

 

では、これから続きの開発をして行きます。

アプリ機能追加は、 ContentView のコードから、

  • Image(systemName: “globe”) Text(“Hello, world!”)

までを削除または追加修正しながら、コードを記述して行く流れになります。

 

単一アプリ向けに、 ContentView にコードを記述して行くのは特に問題は有りませんが

ここでは、複数機能アプリを搭載する事が出来るように、ContentView は、汎用的に

メインメニュー(ランチャー)としての役割を持たせようと思います。

 

メインメニュー(ランチャー)として、ボタンを配置して、別の画面を呼び出す例を紹介します。


import SwiftUI

struct ContentView: View {

    @State var isPopupView_1 = false

    var body: some View {

        VStack{
            Button {
                self.isPopupView_1.toggle()
            } label: {
                Text("QRCodeView")
                    .font(.title3)
            }
            .fullScreenCover(isPresented: $isPopupView_1, content: {
                QRCodeView(isPopupView_1: $isPopupView_1)
            })
            .padding()
        }
    }
}

#Preview {
    ContentView()
}

 

こちらは、メインメニュー(ランチャー)に1つの QRCodeView ボタンが配置されていて、

ボタン押すと、下から別画面(QRCodeView)が出現して、メインメニュー(ランチャー)

上に全て覆い被さるように表示されます(フルモーダル表示)

 

3大UI:ボタン(Button)、テキスト(Text)、イメージ(Image)

上記のコードを、そのままコピー&ペーストして利用する事も出来ますが、せっかくなので、

ボタン(Button)を配置してみます。

 

先ず、ContentView のコードで、

  • Image(systemName: “globe”) ~ Text(“Hello, world!”)

までを削除して、VStack {} のコードにします。

 

{} の中に、カーソルを移動した後、shift + command + L同時に押してみて下さい。

下記のような Library pane(パーツ選択画面が表示されます。

 

上部の左端のアイコンをクリックすると、下記のような Button 等の View Library pane

表示されますので、Button を選択してみて下さい。

 

もちろん、Library pane を利用せず、直接、Butt… タイピングしながら実装する事も

問題有りませんし、Xcode 入力補完機能がサポートされているので、慣れてくると、

直接、タイピングした方が実装は早いかもしれませんね。

 

フルモーダル表示:fullScreenCover()

実装した Button について、簡単に仕様を紹介します、


            Button {
                self.isPopupView_1.toggle()
            } label: {
                Text("QRCodeView")
                    .font(.title3)
            }
            .fullScreenCover(isPresented: $isPopupView_1, content: {
                QRCodeView(isPopupView_1: $isPopupView_1)
            })
            .padding()
  • Button {self.isPopupView_1.toggle()}ボタン押す(タップする)と、isPopupView_1 ステータス反転させています。
  • label: {Text(“QRCodeView“).font(.title3)}ボタンのラベルに、QRCodeViewテキストを設定しています。フォントのサイズは、title3 を指定しています。
  • .fullScreenCover()QRCodeView を呼び出し、フルモーダル表示指定しています。
  • QRCodeView(isPopupView_1: $isPopupView_1)$isPopupView_1 で、遷移先の QRCodeView ture を渡しています。
  • .padding()余白(詰め物)を設定しています。

 

状態管理(Property Wrapper):@State、@Binding

コードの実装から少し離れて、状態管理重要性について、例をあげて紹介します。

 

例えば、新幹線座席予約サイトで、ある日時の新幹線座席予約が完了したとします。

予約一覧画面では予約済みステータスなのに、予約詳細画面では、未予約ステータス

だったり、別ユーザー予約済みステータスの場合、データの整合性が取れておらず、

利用者困惑させてしまいます。

このような問題を起こさないために、状態管理(Property Wrapper)重要となってきます。

 

SwiftUIでは、状態管理(Property Wrapper)に関する機能をいくつか提供しており、

実装する上で、それらを使い分けることが重要です。

親画面子画面孫画面等の複数の画面間で、データの整合性が必要となります。

 

今回は、ContentView(親画面)QRCodeView(子画面)で、

状態管理(Property Wrapper)を設定します。

 

先ずは、ContentView(親画面)で、状態管理(Property Wrapper)を設定します。


    @State var isPopupView_1 = false
  • @State@Stateでマークすると、SwiftUIがストレージの管理を引き継ぎ、を読み書きする手段を提供してくれます。
  • データ値型で、データ更新を行い、データの発生源がView自身の場合は、@Stateが使えます。

 

次に、QRCodeView(子画面)で、状態管理(Property Wrapper)を設定しますが、

こちらは、QRCodeView.swift を作成してからで大丈夫です。


    @Binding var isPopupView_1: Bool
  • @Bindingデータ値型 且つ 読み書き可能 且つ 親Viewで管理されて、双方向データ更新が出来ます。
  • データ値型で、データ更新しますが、データの発生源は、親Viewなど外から渡される場合に、@Bindingが使えます。

 

QRコード生成アプリ

では、QRCode生成アプリを作成して行きます。

 

Xcode の上部タブの File → New → File from Template を選択します。

 

次に、Choose a template for your new file より、SwiftUI View を選択します。

 

SwiftUI View を選択したら、ファイル名:QRCodeView を設定します。

 

Create ボタンを押して、QRCodeView.swift を作成します。

 

QRCodeView:QRコード生成アプリ

次に、ContentView(親画面)から呼び出される QRCodeView(子画面)

ソースを紹介します。

 


import SwiftUI

struct QRCodeView: View {

    @Binding var isPopupView_1: Bool
    @State private var input_url: String = ""

    var body: some View {
        ZStack {
            Color.mint
                .edgesIgnoringSafeArea(.all)

            VStack {
                TextField("URLを入力してください",
                          text: $input_url
                )
                .padding()
                .textFieldStyle(RoundedBorderTextFieldStyle())

                if input_url != "" {

                    generateQRCodeImage(input_url: input_url)
                        .resizable()
                        .scaledToFill()
                        .frame(width: 200, height: 200)
                        .border(Color.black)
                        .padding()

                    HStack(alignment: .center) {

                        Text("URL")
                            .font(.headline)
                            .foregroundColor(.red)

                        Text(input_url)
                            .padding(.vertical, 20)
                            .font(.headline)
                            .foregroundColor(.red)
                            .background(.yellow)
                    }
                }

                Button(action: {
                    withAnimation {
                        input_url = ""
                    }
                }, label: {
                    Text("Input URL Clear")
                        .font(.title2)
                        .foregroundColor(.purple)
                })
                .padding()

                Button(action: {
                    withAnimation {
                        input_url = ""
                        self.isPopupView_1.toggle()
                    }
                }, label: {
                    Text("Close")
                        .font(.title2)
                        .foregroundColor(.black)
                })
                .padding()
            }
        }
    }


    func generateQRCodeImage(input_url: String) -> Image {

        let data = input_url.data(using: .utf8)!

        guard let qrFilter = CIFilter(name: "CIQRCodeGenerator") else {
            return Image(systemName: "xmark.circle.fill")
        }

        qrFilter.setValue(data, forKey: "inputMessage")

        qrFilter.setValue("H", forKey: "inputCorrectionLevel")

        if let outputImage = qrFilter.outputImage {
            let transform = CGAffineTransform(scaleX: 10, y: 10)
            let scaleImage = outputImage.transformed(by: transform)
            
            if let cgImage = CIContext().createCGImage(scaleImage, from: scaleImage.extent) {
                let uiImage = UIImage(cgImage: cgImage)
                return Image(uiImage: uiImage)
            }
        }
        return Image(systemName: "xmark.circle.fill")
    }

}

#Preview {
    QRCodeView(isPopupView_1: .constant(false))
}

 

こちらは、テキストフィールドQRコード画像、入力されたURLClearボタン

Closeボタンが配置されています。

QRCodeView の簡単な仕様は、下記となります。

  • テキストフィールド:任意のURLを入力すると、QRコードを生成し、画面に表示されます。
  • QRコード画像テキストフィールドに任意のURLが入力されると、generateQRCodeImage() が呼び出されて、QRコードを生成します。
  • URLQRコードを生成と同時に、入力されたURLを表示します。長いURLだと、テキストフィールドに全て表示されないので、QRコードの下に全てのURLを表示します。
  • Clearボタン:入力されたテキストフィールドの内容を消去します。
  • CloseボタンQRCodeView(子画面)閉じてContentView(親画面)に戻ります。

 

QRCodeView:Stack View

前回の記事にも紹介しましたが、Stack View について、確認しておきます。

  1. VStack:画面の縦方向(y軸)に、垂直(Vertical)に揃える場合に使用します。
  2. HStack:画面の横方向(x軸)に、水平(Horizontal)に揃える場合に使用します。
  3. ZStack:画面に重ねて(z軸)、奥行きを揃える場合に使用します。

 


    var body: some View {
        ZStack {
            Color.mint
                .edgesIgnoringSafeArea(.all)

            VStack {
                TextField("URLを入力してください",
                          text: $input_url
                )
                .padding()
                .textFieldStyle(RoundedBorderTextFieldStyle())

                if input_url != "" {

                    generateQRCodeImage(input_url: input_url)
                        .resizable()
                        .scaledToFill()
                        .frame(width: 200, height: 200)
                        .border(Color.black)
                        .padding()

                    HStack(alignment: .center) {

                        Text("URL")
                            .font(.headline)
                            .foregroundColor(.red)

                        Text(input_url)
                            .padding(.vertical, 20)
                            .font(.headline)
                            .foregroundColor(.red)
                            .background(.yellow)
                    }
                }

 

  • ZStack {Color.mint.edgesIgnoringSafeArea(.all)メインメニューから呼び出されて、覆い被さるので、メインメニュー区別する為、画面の土台となる部分にミント色を設定しています。土台の上に重なる(z軸)ように、テキストイメージボタンが配置します。
  • VStack{}テキストフィールドQRコード画像、入力されたURLClearボタンCloseボタン縦方向(y軸)に配置しています。
  • HStack(alignment: .center) {URLというタイトル文字と、テキストフィールドに入力されたURLの内容を、水平方向(x軸)に揃えて配置しています。
  • if input_url != “” {テキストフィールドURLが入力されたら、関数generateQRCodeImage() を呼び出して、QRコードを生成します。
  • generateQRCodeImage().frame(width: 200, height: 200) で、QRコードの画像の大きさを設定しています。200ピクセル高さ200ピクセルで設定しています。
  • Text(“URL”)URLの文字を表示します。.foregroundColor(.red) で、テキスト赤字に設定しています。
  • Text(input_url)QRコードの生成と同時に、テキストフィールドに入力されたURLを表示しています。.foregroundColor(.red) では、入力されたURL赤字に設定しています。また、.background(.yellow)は、テキスト背景黄色に設定しています。

 

ボタン(Button):Clear、Close

ボタン(Button)について、紹介します。


                Button(action: {
                    withAnimation {
                        input_url = ""
                    }
                }, label: {
                    Text("Input URL Clear")
                        .font(.title2)
                        .foregroundColor(.purple)
                })
                .padding()

                Button(action: {
                    withAnimation {
                        input_url = ""
                        self.isPopupView_1.toggle()
                    }
                }, label: {
                    Text("Close")
                        .font(.title2)
                        .foregroundColor(.black)
                })
                .padding()

 

  • Input URL ClearボタンButton(action: {withAnimation {input_url = “”}} で、入力されたテキストフィールドの内容を消去します。
  • withAnimation {}  は記述せずに、Button(action: {input_url = “”} のみで、テキストフィールドの内容を消去します。
  • withAnimation {}  は、明示的アニメーションと言われて、ボタン処理を行った際の動作に、アニメーションエフェクト(視覚効果を掛けられますが、処理が速い場合は、あまり視覚効果を確する事が認出来ないと思います。
  • Closeボタン: Button(action: {withAnimation {input_url = “” self.isPopupView_1.toggle()}} で、入力されたテキストフィールドの内容を消去し、isPopupView_1.toggle()ステータス反転させる事で、QRCodeView(子画面)閉じてContentView(親画面)に戻しています。
  • withAnimation {}  は記述せず、Button(action: {input_url = “” self.isPopupView_1.toggle()}で、入力されたテキストフィールドの内容を消去し、QRCodeView(子画面)閉じてContentView(親画面)に戻せます。

 

上記の説明で、withAnimation {} について簡単に触れましたが、SwiftUIアニメーション

については、別の記事で紹介します。

withAnimation {} 明示的アニメーションについては、下記のサイトに詳細が有りますので、

興味のある方は、確認してみて下さい。

 

 

Core Image:画像処理、画像解析

Core Imageは、Apples社OS(iOS/macOS)が提供している画像処理解析を行う為の

フレームワークのひとつです。

Core Imageについては歴史が古く、Mac OS X v10.4(2005年)で導入されています。

主な構成は、下記の3つクラスになります。

  1. CIImage:画像データを管理
  2. CIFilter:画像生成や加工処理(フィルタ)を担当
  3. CIContext:画像の解析や描画等を担当

QRコードを生成する場合は、上記のCIFilterを利用し、CIImageオブジェクトが生成されます。

 

Core Imageについての詳細は、下記を参照してみて下さい。

 

 

 

QRコード生成:CIFilter、CIImage

QRコードを生成する関数(func)generateQRCodeImage() について、紹介します。


    func generateQRCodeImage(input_url: String) -> Image {

        let data = input_url.data(using: .utf8)!

        guard let qrFilter = CIFilter(name: "CIQRCodeGenerator") else {
            return Image(systemName: "xmark.circle.fill")
        }

 

  • func generateQRCodeImage(input_url: String) -> Image:関数のgenerateQRCodeImage()QRコード画像(Image)を生成します。引数は、テキストフィールドに入力された内容(URL)になります。
  • let data = input_url.data(using: .utf8)!テキストフィールドの内容を、UTF8の文字コードで、dataに代入しています。
  • let定数(処理の途中で値を変更しない)を宣言する際に利用します。letは、日本語略で「~とする」の意味で、そのまま変わらない状態を表しています。
  • var変数(処理の途中で値を変更する事が出来る)を宣言する際に利用します。varは、variableの略です。
  • guard let qrFilter = CIFilter(name: “CIQRCodeGenerator”)SwiftUIUIKitObjective-Cでは、CIFilter(name: “CIQRCodeGenerator”) を利用してQRコードを生成することが出来ます。CIFilterで生成される画像データは、CIImageオブジェクトになります。
  • return Image(systemName: “xmark.circle.fill“):QRコード用CIFilterが生成できない場合は、システムシンボル×マーク画像を返しています。

 


        qrFilter.setValue(data, forKey: "inputMessage")
        
        qrFilter.setValue("H", forKey: "inputCorrectionLevel")

 

  • qrFilter.setValue(data, forKey: “inputMessage“):qrFilterinputMessageに、URLが代入したdataを設定しています。
  • qrFilter.setValue(“H“, forKey: “inputCorrectionLevel“):qrFilterinputCorrectionLevelに、QRコード誤り訂正レベル:H(約30%の復元が可能)を設定しています。

 

QRコード生成:CIContext、CGAffineTransform

 


        if let outputImage = qrFilter.outputImage {
            let transform = CGAffineTransform(scaleX: 10, y: 10)
            let scaleImage = outputImage.transformed(by: transform)
            
            if let cgImage = CIContext().createCGImage(scaleImage, from: scaleImage.extent) {
                let uiImage = UIImage(cgImage: cgImage)
                return Image(uiImage: uiImage)
            }
        }
        return Image(systemName: "xmark.circle.fill")
    }

 

  • let transform = CGAffineTransform(scaleX: 10, y: 10):x(水平:横),y(垂直:縦)方向に指定したScaleの倍率:10倍に拡大しています。
  • let scaleImage = outputImage.transformed(by: transform):CIImageはとても小さな画像なので、transform拡大しています。
  • if let cgImage = CIContext().createCGImage(scaleImage, from: scaleImage.extent):CIContext()で、CIImageからCGImage変換しています。
  • UIKitでは、UIImage(ciImage:)で、問題無くQRコードの表示は出来ますが、SwiftUIだとUIImage(ciImage:)では、QRコードの表示が出来ないので、更にCIImageからCGImage変換しています。
  • let uiImage = UIImage(cgImage: cgImage):CGImageUIImage(cgImage:)で、uiImageに渡しています。
  • return Image(uiImage: uiImage):SwiftUIで直接扱えるImage(uiImage:)に変換して、QRコード画像を返しています。

 

SF Symbols:システムシンボル

上記の関数の中で、QRコードの生成が出来ない場合は、下記の説明をしていました。

  • return Image(systemName: “xmark.circle.fill“):QRコード用CIFilterが生成できない場合は、システムシンボル×マーク画像を返しています。

システムシンボルは、Apple社が提供する6,900種類以上アイコンセットです。

Appleプラットフォームシステムフォントである、San Francisco とシームレスに統合

できるようにデザインされています。

システムシンボルを、まだMac環境インストールされていない方は、下記のサイトより、

アプリダウンロードして、インストールしてみて下さい。

 

 

インストールが完了したら、Launchpadを開いてみて下さい。

 

システムシンボル(SF Symbols)アイコンが見つかると思いますので、アイコン

押して(タップして)SF Symbols起動してみて下さい。

 

下記は、SF Symbolsで、該当の xmark.circle.fill アイコンを表示した画面です。

 

QRコード用CIFilterが生成できない場合に表示される、xmark.circle.fill アイコン

確認する事が出来ました。

6,900種類以上アイコン(天気、交通、人、etc)が含まれていますので、他のアプリでも

下記のように記述する事で、簡単に活用できると思います。

  • Image(systemName: “システムシンボル名“)

 

 

アプリの実行:シミュレータの起動

では、実際にアプリを実行してみます。

Xcode の上部タブメニューより、Product → Run を選択します。

 

エラーが無ければ、iPhoneシミュレータが起動します。

ContentView(メインメニュー)に設定した、QRCodeViewボタンが表示されている事が

確認出来ました。

 

QRCodeViewボタン背景色を設定していないので、単なるラベルに見えますが、

上記で紹介した通りボタン(Button)として実装していますので、押してみる(タップする)

ボタン(Button)の挙動をする事も確認出来ると思います。

 

QRCodeView:起動画面

 

では、QRCodeViewボタン押してみます(タップします)

ContentView(メインメニュー)の上に、QRCodeView(QRコード生成アプリ)

覆い被さり、表示された事が確認出来たと思います。

 

ContentView(メインメニュー)明確に区別する為、ZStackで画面の土台となる部分に

設定したミント色が確認出来ますし、テキストフィールドClearボタンCloseボタン

土台の上に重なっている事も確認出来ます。

また、VStackテキストフィールドClearボタンCloseボタン縦方向に配置したので、

こちらも赤枠部分で確認出来ました。

 

QRCodeView:Yahoo! 天気

それでは、テキストフィールドに、任意のURLを入力してみます。

先ずはYahoo! 天気URLを、テキストフィールドに入力してみます。

Yahoo! 天気URLを入力したテキストフィールドの下に、QRコードが表示されている事が

確認出来ます。

また、QRコードの下には、HStackで設定したように、URLテキストテキストフィールド

入力したhttps://weather.yahoo.co.jp/weather/が、水平方向に並んでいる事も確認が

出来ます。

 

 

Clearボタン押す(タップする)と、テキストフィールドが空欄となり、URLテキスト

テキストフィールドに入力したhttps://weather.yahoo.co.jp/weather/が表示されなく

なった事が確認出来ると思います。

 

QRCodeView:X.com

次に、X.comの記事URLを、テキストフィールドに入力してみます。

X.comの記事URLを入力したテキストフィールドの下に、QRコードが表示されている事が

確認出来ます。

また、QRコードの下には、HStackで設定したように、URLテキストテキストフィールド

入力したhttps://x.com/MLB/status/1984837215008981119が、水平方向に並んでいる事が

青枠部分で確認出来ます。

 

 

QRCodeView:Youtube

最後に、YoutubeURLを、テキストフィールドに入力してみます。

YoutubeURLを入力したテキストフィールドの下に、QRコードが表示されている事が

確認出来ます。

また、QRコードの下には、HStackで設定したように、URLテキストテキストフィールド

入力したhttps://www.youtube.com/watch?v=fEvM-OUbaKsが、水平方向に並んでいる事も

確認出来ます。

 

 

iPhoneシミュレータの終了は、Xcodeの上部タブメニューより、Product → Stop

選択すると、実行したアプリが終了します。

 

 

まとめ

今回は、SwiftUIQRコードを生成するアプリケーションを開発する記事を紹介しました。

 

以前、PythonQRコード生成ツールを紹介した記事も同様ですが、厳密に入力されたURL

存在チェックを行っていないので、間違って入力された(存在していない)場合のURL

タイプミスでも、QRコードを生成してしまいます。

しかし、Xcode(IDE)の使い方や下記の内容が習得出来る上に、QRコード生成アプリ

作れてしまいますので、一石二鳥、いや、一石七鳥くらいではないでしょうか…

  1. ContentView:メインメニュー(ランチャー)
  2. 3大UIボタン(Button)テキスト(Text)イメージ(Image)
  3. フルモーダル表示fullScreenCover()
  4. 状態管理(Property Wrapper)@State@Binding
  5. Core Image画像処理解析を行うフレームワーク
  6. QRコード生成CIFilterCIContextCGAffineTransform
  7. SF Symbolsシステムシンボル

 

次回は、SwiftUI色(カラー)を選択するアプリケーションを開発する記事を

紹介しようと思います。

 

コメント

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