🍏

GitHub Copilot for Xcode Chatで何か作る③

2025/02/18に公開

Note(ノート)

GitHub Copilot for Xcode Chatを使用してどれほどのものが作れるのか最近実験をしてみようと思いました。指示を出すと英語で回答されるのですが、日本語の指示は理解できるみたい。

第3弾はお絵描きアプリを作ってみました。プロトタイプを作るときやこんな機能が欲しいというのを生成AIがやってくれるのはありがたいですね。

お絵描きのやり方わからない😅

試しに作って貰ったぞ🔨

おお本当にできた✒️
ボタンを押すと削除されます。

import SwiftUI

struct Stroke: Identifiable {
    var id = UUID()
    var points: [CGPoint]
}

class DrawingViewModel: ObservableObject {
    @Published var strokes: [Stroke] = []

    func addPoint(_ point: CGPoint, to stroke: Stroke) {
        if let index = strokes.firstIndex(where: { $0.id == stroke.id }) {
            strokes[index].points.append(point)
        }
    }

    func addStroke(_ stroke: Stroke) {
        strokes.append(stroke)
    }

    func clear() {
        strokes.removeAll()
    }
}

struct DrawingView: View {
    @StateObject private var viewModel = DrawingViewModel()
    @State private var currentStroke: Stroke?

    var body: some View {
        VStack {
            Canvas { context, size in
                for stroke in viewModel.strokes {
                    var path = Path()
                    if let firstPoint = stroke.points.first {
                        path.move(to: firstPoint)
                        for point in stroke.points.dropFirst() {
                            path.addLine(to: point)
                        }
                    }
                    context.stroke(path, with: .color(.black), lineWidth: 2)
                }
            }
            .gesture(DragGesture(minimumDistance: 0)
                .onChanged { value in
                    let newPoint = value.location
                    if currentStroke == nil {
                        currentStroke = Stroke(points: [newPoint])
                        viewModel.addStroke(currentStroke!)
                    } else {
                        viewModel.addPoint(newPoint, to: currentStroke!)
                    }
                }
                .onEnded { _ in
                    currentStroke = nil
                }
            )
            .background(Color.white)
            .border(Color.black, width: 1)
            .padding()

            Button(action: {
                viewModel.clear()
            }) {
                Text("Clear")
                    .padding()
                    .background(Color.red)
                    .foregroundColor(.white)
                    .cornerRadius(10)
            }
        }
        .padding()
    }
}

#Preview {
    DrawingView()
}


struct ContentView: View {

    var body: some View {
        DrawingView()
    }
}

#Preview {
    ContentView()
}

Que(きっかけ)

GitHub Copilot for Xcode Chatが使えるようになり世の中の人が注目していたので試してみた。
https://zenn.dev/joo_hashi/articles/52ef04563f0728

まさかなぞると線を書いてくれるロジックを作ってくれるとは。。。
頑張ればiPadのお絵描きアプリ作れそうな気がしてきた🔨

Summary(要約)

お絵描きアプリのロジックの解説は以下の通り👇

シンプルなお絵描きアプリの実装解説

このコードはSwiftUIを使用してシンプルなお絵描きアプリケーションを実装しています。ユーザーが画面上でドラッグすることで線を描画でき、描いた内容をクリアすることもできます。

データモデルとビューモデル

Strokeモデル

  • Stroke構造体は1回の連続した描画(ストローク)を表現
  • 各ストロークは一意のID(UUID)と座標点の配列(points)を持つ

DrawingViewModel

  • アプリケーションの状態管理を担当
  • 以下の機能を提供:
    • 新しいストロークの追加
    • 既存ストロークへの点の追加
    • 全ストロークのクリア
  • @Publishedプロパティラッパーを使用して状態の変更を監視可能に

描画機能の実装(DrawingView)

主要コンポーネント

  1. キャンバス領域:

    • SwiftUIのCanvasビューを使用
    • 保存された全ストロークを描画
    • 白背景に黒い境界線を表示
  2. ジェスチャー認識:

    • DragGestureを使用してタッチ/ドラッグを検出
    • ドラッグ開始時に新しいストロークを作成
    • ドラッグ中は座標点を追加
    • ドラッグ終了時にストロークを確定
  3. クリアボタン:

    • 赤背景の「Clear」ボタン
    • タップで全ての描画内容を消去

描画プロセス

  1. ドラッグ開始時:

    • 新しいStrokeインスタンスを作成
    • 最初の点を記録
  2. ドラッグ中:

    • 現在のストロークに新しい点を追加
    • ビューモデルを通じて状態を更新
  3. 描画表示:

    • 各ストロークをパスに変換
    • 黒い実線(太さ2ポイント)で描画

状態管理

  • @StateObjectを使用してビューモデルをビューのライフサイクルに紐付け
  • @Stateで現在描画中のストロークを追跡
  • MVVMパターンに従い、データと表示の責務を分離

このコードは、SwiftUIの宣言的UIプログラミングとモダンな状態管理の特徴を活かしながら、インタラクティブな描画アプリケーションを実現しています。シンプルながら拡張性の高い設計となっています。

Discussion