SwiftUIでお絵かきアプリ

SwiftUIでお絵かきアプリを作りました。
指をなぞると線がかける機能があります。

https://github.com/SatoTakeshiX/EditorApp

ドラッグイベント

画面のドラッグイベントはView.gesture()メソッドにDragGestureインスタンスを引数にわたすと取得できます。DragGestureonChangedメソッドでドラッグ中、onEndedメソッドでドラッグ終了を検知できます。

.gesture(
    DragGesture()
        .onChanged({ (value) in})
        .onEnded({ (value) in})
)

どちらもクロージャーでイベントのValueを取得できます。メンバー変数は次の通り。

  • location: CGPoint, イベントの位置座標
  • startLocation: CGPoint, イベントが始まったときの位置座標

線の引き方

線を引く方法はPathを使います。クロージャーのpathに座標ポイントを渡すと線を引けます。
strokeメソッドで色と線の幅を設定します。

Path { path in
    path.addLines(self.points)//線を引くポイントの配列をパラメーターとして渡す

}
.stroke(self.tmpDrawPoints.color, lineWidth: 10)//

ドラッグ座標の扱い

今回線の色は赤と消しゴム(背景と同じ色指定で消えたようにみせる)の2つを用意しました。
ボタンで色の切り替えができるようにします。

VStack(spacing: 10) {
    Button(action: {
        self.selectedColor = .red

    }) { Text("赤")
    }
    Button(action: {
        self.selectedColor = .clear
    }) { Text("消しゴム")
    }
}
.frame(minWidth: 0.0, maxWidth: CGFloat.infinity)
.background(Color.gray)

ドラッグした場合の位置座標と色を保持するためにDrawPointsという型を定義します。

struct DrawPoints: Identifiable {
    var points: [CGPoint]
    var color: Color
    var id = UUID()
}

DrawPointsをもとにViewを描画するDrawPathViewを定義します。
ZStackForEachを組み合わせてDrawPointsごとにPathを作ります。

struct DrawPathView: View {
    var drawPointsArray: [DrawPoints]
    init(drawPointsArray: [DrawPoints]) {
        self.drawPointsArray = drawPointsArray
    }
    var body: some View {
        ZStack {
            ForEach(drawPointsArray) { data in
                Path { path in
                    path.addLines(data.points)
                }
                .stroke(data.color, lineWidth: 10)
            }
        }
    }
}

お絵かきアプリ

今回のお絵かきアプリのViewをOverlayViewとします。プロパティを@Stateで定義しておきます。

struct OverlayView: View {
    @State var tmpDrawPoints: DrawPoints = DrawPoints(points: [], color: .red)
    @State var endedDrawPoints: [DrawPoints] = []
    @State var startPoint: CGPoint = CGPoint.zero
    @State var selectedColor: DrawType = .red

    var body: some View {}

キャンバスとなるViewをRectangleで作ります。
.overlayDrawPathViewを作りドラッグした際のendedDrawPointsを渡します。
またendedDrawPointsだけだとドラッグが終了しないとPathの描画がされないので描画中に更新されるtmpDrawPointsも利用してPathを描画しておきます。

Rectangle()
    .foregroundColor(Color.white)
    .frame(width: 300, height: 300, alignment: .center)
    .overlay(
        DrawPathView(drawPointsArray: endedDrawPoints)
            .overlay(
                // ドラッグ中の描画。指を離したらここの描画は消えるがDrawPathViewが上書きするので見た目は問題ない
                Path { path in
                    path.addLines(self.tmpDrawPoints.points)
                }
                .stroke(self.tmpDrawPoints.color, lineWidth: 10)
        )
)
    .gesture(
        DragGesture()
            .onChanged({ (value) in
          // ドラッグごとのイベントのみを更新する
                if self.startPoint != value.startLocation {
                    self.tmpDrawPoints.points.append(value.location)
                    self.tmpDrawPoints.color = self.selectedColor.color

                }
            })
            .onEnded({ (value) in
                self.startPoint = value.startLocation
                self.endedDrawPoints.append(self.tmpDrawPoints)
                self.tmpDrawPoints = DrawPoints(points: [], color: self.selectedColor.color)
            })
)

まとめ

簡単ですが、指でお絵かきアプリが作れました。
まとめます。

  • .gesture(DragGesture())でドラッグイベントを取得する
  • Path { path in }で線を引く
  • ドラッグイベントごとに座標を保持する
  • Viewに描画

宣伝

インプレスR&D社より、「1人でアプリを作る人を支えるSwiftUI開発レシピ」発売中です。
「SwiftUIでアプリを作る!」をコンセプトにSwiftUI自体の解説とそれを組み合わせた豊富なサンプルアプリでどんな風にアプリ実装すればいいかが理解できる本となっています。
iOS 14対応、Widgetの作成も一章まるまるハンズオンで解説しています。
SwiftUIを学びたい方、ぜひこちらのリンクをチェックしてください!




https://nextpublishing.jp/book/12491.html