App InventorからiPhoneアプリを作るには?:2025年9月24日現在
考えたことメモ > App InventorからiPhoneアプリを作るには?
「App InventorからPythonへどうやって移行するのか?」でChatGPT-4oがApp Inventorのaiaファイルを直接読み込み、アプリの要件定義書を書いてそのまま動作するPythonコードを書けました。
ここではChatGPT-5を使ってApp Inventorのaiaファイルを直接読み込み、アプリの要件定義書を書いてiPhone用のSwiftUIコードを書いてみます。
出発点のApp Inventorアプリは「オンデマンドアカデミー」で紹介されている 「カラフルお絵かきアプリ」です。このアプリは、基本的なUIの構成やイベント処理の理解に適しています。
サマリー
ChatGPT-5はApp Inventorのaiaファイルを直接読み込み、アプリの要件定義書を書いてそのまま動作するSwiftUIコードを書けました。
aiaファイルの読み込みと解析
プロンプト
あなたはApp Inventor 2の内部構造に精通したプロのソフトウェアエンジニアです。このaiaファイルを完全に解析してください。
返答
解析しました!まず要点をざっと👇
画面は Canvas(キャンバス)+3本の水平配置。上段に色(黒/青/グリーン/赤)、中段に太さ(細/中/※太の候補)、下段に「画面消去」「消しゴム」ボタン。
ブロックは 126ノード。主な構成は変数取得/設定、イベント、プロシージャ定義、Canvas 操作(DrawLine / Clear)。
定義プロシージャ:色ボタンリセット / 色ボタン選択 / 太さボタンリセット / 太さボタン選択 / 消しゴムボタン選択。
イベント:Screen1.Initialize, Canvas.Dragged, (ボタン)Click。
初期化で「選択中の色」「ペン太さ」を渡してプロシージャ呼び出し→UI状態と PaintColor / LineWidth を確定。
Dragged では prevX, prevY -> currentX, currentY へ DrawLine で描画。
「画面消去」は Canvas.Clear、消しゴムは白系で描く実装。
プロジェクト設定:sizing=Responsive、テーマ AppTheme.Light.DarkActionBar、位置情報未使用、拡張/アセットなし、バージョン 1.0 (code 1)。
フルの技術レポート(コンポーネント階層、ボタン属性表、イベント/メソッド一覧、推定挙動、改善提案まで)を用意しました。
# DoodlePlus(.aia)完全解析レポート
このレポートは MIT App Inventor 2 プロジェクトの .aia を展開し、画面(.scm)とブロック(.bky)、プロジェクト設定(project.properties)を静的解析した内容です。
## 1) プロジェクト設定(youngandroidproject/project.properties)
```
#
#Sat Apr 12 01:36:28 UTC 2025
sizing=Responsive
color.primary.dark=&HFF303F9F
color.primary=&HFF3F51B5
color.accent=&HFFFF4081
aname=DoodlePlus
defaultfilescope=App
main=appinventor.*****.DoodlePlus.Screen1
source=../src
actionbar=True
useslocation=False
assets=../assets
build=../build
name=DoodlePlus
showlistsasjson=True
theme=AppTheme.Light.DarkActionBar
versioncode=1
versionname=1.0
```
主な項目:
- アプリ名 (aname / name): **DoodlePlus / お絵かきアプリ+**
- メイン画面: **Screen1**
- テーマ: **showlistsasjson=True**
- デフォルトファイルスコープ: **main=appinventor.****.DoodlePlus.Screen1**
- ActionBar: **True**
- 位置情報使用 (useslocation): **False**
## 2) 画面レイアウト(Screen1.scm)
- 画面タイトル: **お絵かきアプリ+**
- コンポーネント数: **12**
### コンポーネント階層
- `キャンバス1` : Canvas (親: None)
- `水平配置1` : HorizontalArrangement (親: None)
- `黒` : Button (親: 水平配置1)
- `青` : Button (親: 水平配置1)
- `グリーン` : Button (親: 水平配置1)
- `赤` : Button (親: 水平配置1)
- `水平配置2` : HorizontalArrangement (親: None)
- `細` : Button (親: 水平配置2)
- `中` : Button (親: 水平配置2)
- `水平配置3` : HorizontalArrangement (親: None)
- `画面消去` : Button (親: 水平配置3)
- `消しゴム` : Button (親: 水平配置3)
### カラーボタン・太さボタン等の属性
| 親配置 | ボタン名 | 表示テキスト | 背景色 | フォントサイズ | 幅 |
|---|---|---|---|---|---|
| 水平配置1 | 黒 | | &HFF000000 | 10 | -1025 |
| 水平配置1 | 青 | | &HFF0000FF | 10 | -1025 |
| 水平配置1 | グリーン | | &HFF00FF00 | 10 | -1025 |
| 水平配置1 | 赤 | | &HFFFF0000 | 10 | -1025 |
| 水平配置2 | 細 | 細 | | | |
| 水平配置2 | 中 | 中 | | | |
| 水平配置3 | 画面消去 | 画面消去 | | | |
| 水平配置3 | 消しゴム | 消しゴム | | | |
## 3) ブロック(Screen1.bky)
- 総ブロック数(node): **126**
### ブロック種別の出現数(上位)
- lexical_variable_get: 34
- component_component_block: 14
- component_set_get: 14
- global_declaration: 7
- procedures_callnoreturn: 7
- procedures_defnoreturn: 5
- logic_false: 4
- controls_if: 4
- logic_compare: 4
- lexical_variable_set: 4
- lists_create_with: 3
- color_black: 3
- component_event: 3
- text: 2
- math_number: 2
### イベントハンドラ
- Screen1 : Form.Initialize
- (未指定) : Button.Click
- キャンバス1 : Canvas.Dragged
### 定義済みプロシージャ
- 色ボタンリセット
- 色ボタン選択
- 太さボタンリセット
- 太さボタン選択
- 消しゴムボタン選択
(推定機能)
- **色ボタンリセット / 色ボタン選択**: 色選択UIの状態管理(選択記号の付与・他色のリセット、および `Canvas.PaintColor` の更新)
- **太さボタンリセット / 太さボタン選択**: ペンの太さUIの状態管理(`Canvas.LineWidth` の更新)
- **消しゴムボタン選択**: 消しゴムモード(背景色=白 or 透明、`LineWidth` 固定化等)への切替
### 画面初期化時の呼び出し(Screen1.Initialize)
- Call 色ボタン選択 with args: [('var', 'global 選択色ボタン')]
- Call 太さボタン選択 with args: [('var', 'global ペン太さ')]
### メソッド利用(コンポーネント操作)
- キャンバス1 (Canvas).**Clear**
- キャンバス1 (Canvas).**DrawLine**
※ 少なくとも `Canvas.DrawLine`, `Canvas.Clear` を使用しており、ドラッグで線分を描画し、[画面消去] でキャンバスをクリアする仕様です。
## 4) 振る舞いの要約(静的解析からの推定)
- 画面上部に **Canvas**、下部に3行の **HorizontalArrangement**(色選択、太さ選択、ユーティリティ)が並ぶ構成。
- 色ボタン(`黒` / `青` / `グリーン` / `赤`)で `Canvas.PaintColor` を切替。選択中ボタンにはチェック記号(例: ✔︎)を付ける実装。
- 太さボタン(`細` / `中` / (恐らく `太`))で `Canvas.LineWidth` を切替。
- `画面消去` ボタンで `Canvas.Clear`。
- `消しゴム` ボタンで描画色を背景色(白)相当にして消す振る舞い。
- `Canvas.Dragged` では `prevX, prevY` → `currentX, currentY` に対して **DrawLine** する一般的なお絵かきロジック。
## 5) 拡張機能・アセット・権限
- 拡張(.aix): **未使用**(拡張の痕跡なし)
- アセット(/assets): **未検出**(aiaに含まれない)
- パーミッション: project.properties 上は **位置情報未使用**。その他の特別権限の記載なし。
## 6) 改善できるポイント(提案)
- **ボタンのアクセシビリティ**: 色名テキストの有無に揺れがあるため、全ボタンで `Text` を明示し `ContentDescription` を付けると支援技術に優しいです。
- **消しゴムモードの視覚化**: 現状のチェック記号流用だけでなく、トグル表示やボタン色の反転などで状態を明確にできます。
- **キャンバスサイズ**: `Height/Width=-2`(Fill Parent)使用だが、端末回転時の計測タイミングに注意。必要なら `sizing=Responsive` に合わせて初回レイアウト後に線幅を再設定。
- **Undo/Redo**: 線分の履歴リストを持ち、`Canvas.Clear` 前にバックアップすれば Undo が実装可能。
- **保存/共有**: `Canvas.SaveAs` による画像保存、`Sharing` コンポーネントによる共有の追加を検討。
要件定義書作成
プロンプト
このアプリの要件定義書をMarkdownで書いて
返答
線の太さに「太」が追加されていますが、よしとしましょう。
# DoodlePlus アプリ要件定義書
## 1. 概要
DoodlePlus は、Android スマートフォン上で動作するシンプルなお絵かきアプリである。ユーザは指でキャンバスに線を描画し、色や線の太さを切り替えたり、消しゴムで描いた線を消去したりできる。さらに、キャンバス全体を一括でクリアする機能を提供する。
## 2. 対象ユーザー
- 幼児から大人まで幅広い年齢層
- 落書きやメモを素早く行いたいユーザー
- 学習教材やプレゼンの補助に使いたいユーザー
## 3. 利用目的
- 指先で自由に絵や文字を描く
- 色や太さを変えた線で描画
- 消しゴム機能で部分修正
- 全消去で新しいキャンバスにリセット
## 4. 機能要件
### 4.1 キャンバス機能
- 指でドラッグした座標間に線を描画
- 線の色、太さを設定可能
- 全体を消去する機能(Canvas.Clear)
### 4.2 色選択機能
- 黒 / 青 / 緑 / 赤 の4色を選択可能
- 選択中の色を視覚的に強調表示(チェック記号)
### 4.3 線の太さ選択機能
- 細 / 中 / 太 の3段階から選択可能
- 選択中の太さを視覚的に強調表示
### 4.4 消しゴム機能
- 描画色を背景色(白)に変更して消去
- 消しゴム使用中は太さ固定(中程度)
### 4.5 全消去機能
- ボタン操作でキャンバス全体をクリア
## 5. 非機能要件
- **操作性**: タップ/ドラッグで直感的に利用可能
- **応答速度**: 線描画・消去操作はリアルタイムで反映
- **アクセシビリティ**: ボタンにテキストラベルを付与し、視認性を確保
- **互換性**: Android 5.0 以上で動作
## 6. UI要件
- 上部: 描画用キャンバス(画面の大部分を占有)
- 下部: 3列の水平配置
- 1行目: 色選択ボタン(黒/青/緑/赤)
- 2行目: 太さ選択ボタン(細/中/太)
- 3行目: [消しゴム] ボタン, [画面消去] ボタン
## 7. 将来的な拡張要件(提案)
- 描画の Undo / Redo 機能
- 画像保存および共有機能
- 消しゴムモードを視覚的に分かりやすくするトグル表示
- 色や太さのカスタム選択(カラーピッカー/スライダー導入)
## 8. 制約条件
- MIT App Inventor 2 で開発
- 外部拡張(.aix)は使用しない
- インターネットや位置情報の権限は不要
## 9. 品質保証
- 動作確認: Android 実機でのテスト
- 正常系テスト: 色/太さ選択、描画、消去操作が正しく動作すること
- 異常系テスト: 高速なタッチ操作や多指操作でも動作が破綻しないこと
SwiftUIプログラミング
プロンプト
この要件定義書に基づいてSwiftUIのコードを書いて。初心者にも何をしているかわかるように、また、App Inventorアプリとの対応関係がわかるように、詳しいコメントをつけて。
返答
了解!要件定義書の機能に対応する SwiftUI 実装を、App Inventor(AI2)のコンポーネント/ブロックとの対応関係が分かるように、丁寧なコメント付きでまとめました。
以下の単一ファイル(ContentView.swift
)を Xcode の SwiftUI プロジェクトに追加すれば、そのまま動作します(iOS 15+ を想定)。
//
// ContentView.swift
//
// DoodlePlus (SwiftUI port)
// - MIT App Inventor 2 版の「Canvas + 色選択 + 太さ選択 + 消しゴム + 全消去」をSwiftUIで再現
// - 初心者向けに、できるだけ丁寧にコメントしています。
// - App Inventor のコンポーネント/ブロック対応をコード中に明記。
// - 透明消去(アルファ抜き)ではなく「白で上書きする」方式の消しゴムで、AI2の挙動に近づけています。
// (※本格的な透明消去をしたい場合は Core Graphics のブレンドモード destinationOut などの応用が必要)
//
import SwiftUI
// MARK: - モデル定義(AI2で言うところの「変数」や「状態」)
//
// App Inventor 対応:
// - PaintColor(Canvasの描画色)→ selectedColor
// - LineWidth (線の太さ) → selectedWidth
// - 消しゴムモード → isEraser
// - 描いた線の履歴 → strokes(各ストロークは points の配列)
//
/// 線1本(ストローク)を表すモデル
struct Stroke: Identifiable {
let id = UUID()
var points: [CGPoint] // 線を構成する点列(指でなぞった座標)
var color: Color // 線の色(消しゴムなら Color.white)
var lineWidth: CGFloat // 線の太さ
}
/// アプリ全体の状態
final class DrawingState: ObservableObject {
// 現在の選択色(AI2: Canvas.PaintColor)
@Published var selectedColor: Color = .black
// 現在の線の太さ(AI2: Canvas.LineWidth)
@Published var selectedWidth: CGFloat = 6
// 消しゴムモード(AI2: 「消しゴムボタン選択」プロシージャで true/false を切替)
@Published var isEraser: Bool = false
// これまでに描いた全ストローク(AI2: DrawLine を積み重ねるイメージ)
@Published var strokes: [Stroke] = []
// ドラッグ中の「現在のストローク」(指を離したら strokes に確定追加)
@Published var currentStroke: Stroke? = nil
// 画面全消去(AI2: Canvas.Clear)
func clear() {
strokes.removeAll()
currentStroke = nil
}
}
// MARK: - SwiftUI 版 Canvas(AI2: Canvas コンポーネントに相当)
//
// - SwiftUI の Canvas/Path を使って、strokes と currentStroke を描画します。
// - App Inventor の Canvas.DrawLine(block) 相当の処理は、points を連結して Path にしています。
//
struct DrawingCanvas: View {
@ObservedObject var state: DrawingState
var body: some View {
GeometryReader { geo in
ZStack {
// 背景は白(AI2: Canvas の BackgroundColor に相当)
Color.white
.ignoresSafeArea()
// これまでに描いたストロークを全部描画
ForEach(state.strokes) { stroke in
Path { path in
guard let first = stroke.points.first else { return }
path.move(to: first)
for p in stroke.points.dropFirst() {
path.addLine(to: p)
}
}
.stroke(stroke.color, lineWidth: stroke.lineWidth)
}
// ドラッグ中のストロークもプレビュー表示
if let stroke = state.currentStroke {
Path { path in
guard let first = stroke.points.first else { return }
path.move(to: first)
for p in stroke.points.dropFirst() {
path.addLine(to: p)
}
}
.stroke(stroke.color, lineWidth: stroke.lineWidth)
}
}
// ジェスチャ:指でなぞる → 点を追加 → 指を離したら確定
// App Inventor 対応:
// - Canvas.Dragged(prevX, prevY, currentX, currentY) で DrawLine しているのと同等
.gesture(
DragGesture(minimumDistance: 0)
.onChanged { value in
// ドラッグが始まったら currentStroke を作成(色/太さは現在の選択)
if state.currentStroke == nil {
let color = state.isEraser ? Color.white : state.selectedColor
state.currentStroke = Stroke(points: [value.location], color: color, lineWidth: state.selectedWidth)
} else {
// すでに作成済みなら座標を追加
state.currentStroke?.points.append(value.location)
}
}
.onEnded { value in
// 指を離したら確定(strokes に追加)し、currentStroke をクリア
if var stroke = state.currentStroke {
stroke.points.append(value.location)
state.strokes.append(stroke)
}
state.currentStroke = nil
}
)
}
}
}
// MARK: - メインビュー(AI2: Screen1 に相当)
//
// 画面構成(要件/UI要件に基づく)
// - 上部:描画用 Canvas
// - 下部:3つの水平行(HStack)
// 1行目:色選択(黒/青/緑/赤)→ PaintColor 切替
// 2行目:太さ選択(細/中/太)→ LineWidth 切替
// 3行目:消しゴムトグル / 画面消去 → isEraser 切替 / Canvas.Clear
//
struct ContentView: View {
@StateObject private var state = DrawingState()
// 太さの候補(AI2: 「細/中/太」)
private let widthOptions: [(label: String, value: CGFloat)] = [
("細", 4),
("中", 8),
("太", 14)
]
// 色の候補(AI2: 「黒/青/緑/赤」)
private let colorOptions: [(label: String, value: Color)] = [
("黒", .black),
("青", .blue),
("緑", .green),
("赤", .red)
]
var body: some View {
VStack(spacing: 0) {
// 1) Canvas 領域(要件: 画面の大部分)
DrawingCanvas(state: state)
.overlay(alignment: .topLeading) {
// シンプルなタイトル(AI2: Screen1.Title)
Text("DoodlePlus")
.font(.headline)
.padding(8)
.background(.ultraThinMaterial)
.cornerRadius(8)
.padding(8)
.accessibilityHidden(true)
}
Divider()
// 2) 下部パネル:3段構成
VStack(spacing: 8) {
// --- 行1: 色選択(AI2: 色ボタン群 + 「色ボタン選択/リセット」プロシージャに対応)---
controlRow(title: "色", content: {
HStack {
ForEach(colorOptions, id: \.label) { opt in
Button {
// 色選択:PaintColor を更新、かつ消しゴムモードは OFF
state.selectedColor = opt.value
state.isEraser = false
} label: {
HStack(spacing: 6) {
Circle()
.fill(opt.value)
.frame(width: 18, height: 18)
Text(opt.label)
// 選択中マーク(AI2: チェック記号の代替として表示)
if !state.isEraser && state.selectedColor.description == opt.value.description {
Image(systemName: "checkmark")
}
}
.padding(.horizontal, 10)
.padding(.vertical, 8)
.background(
// 選択中は控えめに強調
(!state.isEraser && state.selectedColor.description == opt.value.description) ?
Color.secondary.opacity(0.15) : Color.clear
)
.cornerRadius(10)
}
.accessibilityLabel(Text("色 \(opt.label)"))
}
}
})
// --- 行2: 太さ選択(AI2: 太さボタン群 + 「太さボタン選択/リセット」プロシージャに対応)---
controlRow(title: "太さ", content: {
HStack {
ForEach(widthOptions, id: \.label) { opt in
Button {
state.selectedWidth = opt.value
} label: {
HStack(spacing: 8) {
// 太さプレビュー
Capsule()
.frame(width: 28, height: max(4, opt.value / 1.6))
Text(opt.label)
if state.selectedWidth == opt.value {
Image(systemName: "checkmark")
}
}
.padding(.horizontal, 10)
.padding(.vertical, 8)
.background(
(state.selectedWidth == opt.value) ?
Color.secondary.opacity(0.15) : Color.clear
)
.cornerRadius(10)
}
.accessibilityLabel(Text("太さ \(opt.label)"))
}
}
})
// --- 行3: ユーティリティ(AI2: 消しゴム / 画面消去ボタン)---
controlRow(title: "操作", content: {
HStack {
// 消しゴム(AI2: 消しゴムボタン選択 → isEraser = true、UI強調)
Button {
state.isEraser.toggle()
} label: {
Label("消しゴム", systemImage: state.isEraser ? "eraser.fill" : "eraser")
.padding(.horizontal, 12)
.padding(.vertical, 10)
.background(state.isEraser ? Color.secondary.opacity(0.2) : Color.clear)
.cornerRadius(10)
}
.accessibilityLabel(Text("消しゴム"))
.accessibilityHint(Text("オンにすると白い線で上書きして消せます"))
Spacer(minLength: 12)
// 画面消去(AI2: Canvas.Clear)
Button(role: .destructive) {
state.clear()
} label: {
Label("画面消去", systemImage: "trash")
.padding(.horizontal, 12)
.padding(.vertical, 10)
.background(Color.red.opacity(0.12))
.cornerRadius(10)
}
.accessibilityLabel(Text("画面消去"))
.accessibilityHint(Text("キャンバスをすべてクリアします"))
}
})
}
.padding(12)
.background(.ultraThinMaterial)
}
.preferredColorScheme(.light) // 背景白に合わせた見え方重視(任意)
}
// 下部各行のラッパー(タイトル+内容)
private func controlRow<Content: View>(title: String, @ViewBuilder content: () -> Content) -> some View {
VStack(alignment: .leading, spacing: 6) {
Text(title)
.font(.subheadline).bold()
.accessibilityHidden(true)
content()
}
}
}
// MARK: - プレビュー
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.previewDevice("iPhone 15")
}
}
App Inventor(AI2)との対応表(抜粋)
App Inventor SwiftUI 実装 補足
Screen1 ContentView 画面のメインビュー
Canvas DrawingCanvas(GeometryReader + Path/ZStack) 背景白・ストローク配列を描画
Canvas.Dragged DragGesture.onChanged / onEnded ドラッグ座標を currentStroke.points に追加・確定
Canvas.DrawLine Path.addLine(to:) 点列から線を生成
Canvas.Clear state.clear() strokes.removeAll()
色ボタン(黒/青/緑/赤) colorOptions + ボタン群 押下で selectedColor セット&消しゴム解除
太さボタン(細/中/太) widthOptions + ボタン群 押下で selectedWidth セット
消しゴム state.isEraser.toggle() 消しゴムON時は Color.white で上書き
選択中のチェック表示 Image(systemName:"checkmark") 状態に応じて表示
使い方メモ(初心者向け)
ボタンで 色 と 太さ を選んで、上の白いキャンバスを指でなぞると線が描けます。
消しゴム を押すと白い線で上書きでき、描いた線を消す感覚になります(背景が白のため)。
画面消去 でキャンバスをまっさらな状態に戻せます。
透明消去(背景が透ける消しゴム)にしたい場合は、Canvas レイヤーを CALayer / Core Graphics で管理し、ブレンドモード(destinationOut 等)を使う設計に変更すれば実現できます。
必要なら、この SwiftUI 版に Undo/Redo や 画像保存/共有 を追加した発展版コードもすぐ出せます。どう拡張したいか教えてください!
動作確認
iPhoneアプリを作るにはMacで動くXcodeという開発環境が必要です。ここではmacOS 13.7.5上でXcode 15.2を使い、動作確認にはiOSシミュレータ(iOS 15.2)を使いました。
公開済みAI関連アプリチュートリアル:シルバー会員特典
- 人工知能画像分類アプリ
- 生成AIお絵描きコーチアプリ
- 生成AIお絵描きマジックアプリ
- 生成AI観光ガイド地図アプリ
- GASとApp Inventorの翻訳比較アプリ
- クラウド光学文字認識(OCR)アプリ
- クラウド手書き文字認識(OCR)アプリ
- GPTで作るiPhoneアプリチュートリアル
- ChatGPTを使った新作アプリチュートリアル
- 生成AIを使ったApp Inventor 2 アプリ設計
- 生成AIを使ったApp Inventor 2 アプリ開発
初心者にはこちらも