前回は、Xcode(IDE)でプロジェクト作成を行う記事を紹介しました。
今回は、SwiftUIでQRコードを生成するアプリケーションを開発する記事を
紹介しようと思います。
以前、PythonでQRコード生成ツールを紹介しましたので、興味のある方は、
下記を参照してみて下さい。
習得内容
開発言語を習得する場合、基本文法やコンポーネントの使い方などを机上で学んでから、
開発を進める事が多い(王道?)と思いますが、こちらでは、実際にSwifUIを記述しながら、
習得して行きます。
今回、SwiftUIでQRコードを生成するアプリケーションを生成する事で、下記の内容を
習得する事が出来ます。
- ContentView:メインメニュー(ランチャー)
- 3大UI:ボタン(Button)、テキスト(Text)、イメージ(Image)
- フルモーダル表示:fullScreenCover()
- 状態管理(Property Wrapper):@State、@Binding
- Core Image:画像処理や解析を行うフレームワーク
- QRコード生成:CIFilter、CIContext、CGAffineTransform
- SF Symbols:システムシンボル
動作環境
先ずは、アプリが動作する環境を紹介します。
- mac OS:Sequoia 15.6.1
- iOS:26.0
- Xcode:26.0.1
- Swift:6.2.1
ContentView:メインメニュー(ランチャー)
前回の記事では、Xcode(IDE)にて、SwiftMenu というプロジェクト作成をしましたので、
下記のプログラムが自動生成されていると思います。
- SwiftMenuApp:初期起動プログラム → ContentView を呼び出す
- 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コード画像、入力されたURL、Clearボタン、
Closeボタンが配置されています。
QRCodeView の簡単な仕様は、下記となります。
- テキストフィールド:任意のURLを入力すると、QRコードを生成し、画面に表示されます。
- QRコード画像:テキストフィールドに任意のURLが入力されると、generateQRCodeImage() が呼び出されて、QRコードを生成します。
- URL:QRコードを生成と同時に、入力されたURLを表示します。長いURLだと、テキストフィールドに全て表示されないので、QRコードの下に全てのURLを表示します。
- Clearボタン:入力されたテキストフィールドの内容を消去します。
- Closeボタン:QRCodeView(子画面)を閉じて、ContentView(親画面)に戻ります。
QRCodeView:Stack View
前回の記事にも紹介しましたが、Stack View について、確認しておきます。
- VStack:画面の縦方向(y軸)に、垂直(Vertical)に揃える場合に使用します。
- HStack:画面の横方向(x軸)に、水平(Horizontal)に揃える場合に使用します。
- 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コード画像、入力されたURL、Clearボタン、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つのクラスになります。
- CIImage:画像データを管理
- CIFilter:画像生成や加工処理(フィルタ)を担当
- 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”):SwiftUIやUIKit、Objective-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“):qrFilterのinputMessageに、URLが代入したdataを設定しています。
- qrFilter.setValue(“H“, forKey: “inputCorrectionLevel“):qrFilterのinputCorrectionLevelに、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):CGImageをUIImage(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
最後に、YoutubeのURLを、テキストフィールドに入力してみます。
YoutubeのURLを入力したテキストフィールドの下に、QRコードが表示されている事が
確認出来ます。
また、QRコードの下には、HStackで設定したように、URLのテキストとテキストフィールドに
入力したhttps://www.youtube.com/watch?v=fEvM-OUbaKsが、水平方向に並んでいる事も
確認出来ます。
iPhoneシミュレータの終了は、Xcodeの上部タブメニューより、Product → Stop を
選択すると、実行したアプリが終了します。
まとめ
今回は、SwiftUIでQRコードを生成するアプリケーションを開発する記事を紹介しました。
以前、PythonでQRコード生成ツールを紹介した記事も同様ですが、厳密に入力されたURLの
存在チェックを行っていないので、間違って入力された(存在していない)場合のURLや
タイプミスでも、QRコードを生成してしまいます。
しかし、Xcode(IDE)の使い方や下記の内容が習得出来る上に、QRコード生成アプリも
作れてしまいますので、一石二鳥、いや、一石七鳥くらいではないでしょうか…
- ContentView:メインメニュー(ランチャー)
- 3大UI:ボタン(Button)、テキスト(Text)、イメージ(Image)
- フルモーダル表示:fullScreenCover()
- 状態管理(Property Wrapper):@State、@Binding
- Core Image:画像処理や解析を行うフレームワーク
- QRコード生成:CIFilter、CIContext、CGAffineTransform
- SF Symbols:システムシンボル
次回は、SwiftUIで色(カラー)を選択するアプリケーションを開発する記事を
紹介しようと思います。
- イラスト:いらすとや より引用





























コメント