LoginSignup
0
1

More than 1 year has passed since last update.

[MacOS]SwiftUI、非同期での図形描画

Last updated at Posted at 2022-01-05

 ターゲットをMacOSとしたSwiftUIで、マルチスレッド利用した重たい処理中に図形を描画するサンプルです。

やりたいこと

 Mac環境において、重たい処理中でも処理経過などをグラフィカルにSwiftUIで簡単に表示したい。

サンプルのイメージ

 グラフ描画開始ボタンをクリックするとウインドウ上に折れ線グラフっぽいグラフが1秒間隔で描画されます(重たい処理での計算途中結果を自動表示したいケースをイメージ)。

SwiftUI利用した計算途中状況などの図形描画サンプルが少なかったので、作成してみました。
スクリーンショット 2022-01-05 17.33.35.png

サンプル作成の環境

 Swift 5.5.2, Xcode 13.2.1, M1 Mac

コードサンプル

 MacOSをターゲットとしたSwiftUIプロジェクトで、デフォルトで作成されるソースに以下上書きすると動作するはずです。処理内容など、細かい点はコード上のコメントを参照ください。
なお、変数の排他制御は必要になることがあります。

ContentView.swift
// 描画データの構造体
struct MyData {
    var x: CGFloat
    var y: CGFloat
}

import SwiftUI

struct ContentView: View {
    // 描画データの配列:「@State」定義の変数に変化があると画面は自動的に再描画される。
    @State var lineData: [MyData] = []
    @State var buttonEnable = true

    var body: some View {
        VStack {
            // 描画先のコンテキスト
            Canvas { context, size in
                // 外部関数呼び出し、描画するパスがセットされる
                let myPath = drawLine(view: self)
                // myPathにセットされた情報で描画する
                context.stroke(myPath,
                               with        : .color(.orange),
                               lineWidth   : 2)
            }
            .frame(width: 400, height: 300)
            .border(Color.blue)
            .background(Color.white)

            // 描画するためのアクション
            //  - 10秒間バックグランドでの処理結果を表示させる
            Button("グラフ描画開始") {
                // 非同期処理開始(スレッド登録&実行)
                DispatchQueue(label: "runTask", attributes: .concurrent).async {
                    // ボタン非活性化
                    self.buttonEnable = false
                    // 外部関数呼び出し( 重たい処理をイメージ、内部で10秒間処理している )
                    runTask(view: self)
                    // ボタン活性化
                    self.buttonEnable = true
                }
            }
            .padding()
            .disabled(!buttonEnable)        // ボタンの活性・非活性を変数で制御
        }
    }
}

// 重たい処理で描画データセットっぽくしたもの:通常は計測データなどセット
func runTask(view: ContentView) {
    // サンプルにランダムなデータを生成(説明略)
    view.lineData = []
    for n in 0 ... 10 {
        sleep(1)
        let x = CGFloat( n * 40 )
        let y = CGFloat.random(in: 50 ..< 250)
        let line = MyData(x: x,
                          y: y)
        // 適宜 変数などの排他ロックを行う
        view.lineData += [line]
    }
}

// データをパスとして描画
func drawLine(view: ContentView) -> Path {
    // 描画パス
    var myPath = Path()
    // 初期位置セット(キャンパスにペンを置く感じ)
    myPath.move(to: CGPoint(x: 0,
                            y: 0))
    // 配列にセットされたデータで描画
    for data in view.lineData {
        // データ毎にペンを移動させて描く
        myPath.addLine(to: CGPoint(x: data.x,
                                   y: data.y))
    }
    // 描画パスを返す
    return myPath
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

投稿について

 SwiftUIは経験数週間ですが、MacOSをターゲットとした記事が少ないので投稿してみました。間違いやもっと良い方法もあるかと思いますが、開発の参考になればと思います。

この方法は、Swiftでシミュレーションや計測などプログラミングした時の結果表示に使っています。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1