SwiftUI:iOS16から追加されたChartsでグラフを作成

  • このエントリーをはてなブックマークに追加

iOS16、Xcode14から標準グラフ描画ライブラリにChartsが追加されました。ここではまだ情報が少ないChartsを使って、技術プログラムでは必須の散布図や折れ線グラフを作成する方法を紹介します。SwiftUI初心者の筆者が調べながら書いているので、同じく初心者の人に参考になると思います。

こんにちは。wat(@watlablog)です。ここではSwiftUI標準のChartsを使ってグラフを作ってみます

SwiftUI標準Chartsの概要と本記事の目標

標準のCharts

この記事で扱うのはSwiftUI標準で使えるChartsです。標準のChartsについては以下のリンク先を参考にしてください。
https://developer.apple.com/jp/xcode/swiftui/

SwiftUIのChartsについて、公式ページはこちらです。基本は1次情報であるこのページを参照しておきましょう。
https://developer.apple.com/documentation/charts

ちなみに標準のChartsが2022年9月に発表されるまではGitHubのページからダウンロードしてグラフを描画する方法があったようです。筆者は2023年からSwiftに入門したのですが、Web検索だけだとどちらのChartsについて書いてある記事なのかよくわからない時がありましたのでご注意ください。
https://github.com/danielgindi/Charts

目標:散布図と折れ線グラフをつくる

「SwiftUI Charts」で検索するとどうやら棒グラフの描き方ばかり出てきます。おそらくiOSアプリとしてはファイル容量や日々のデータ可視化等、棒グラフに適したデータにグラフを用いるニーズが多いためであると考えられます。
さらに、サンプルの多くはデータを事前に用意しておいてそれを表示させるという内容が多いようです。

WATLABブログは主にPythonで科学技術プログラミングを学ぶための記事を書いてきましたが、その知見をiOSアプリにしてみたいと思ったことがSwiftを学んでいる理由です。
そのためSwiftでも科学技術でよく使う散布図折れ線グラフを作ってみたいと思います。

また技術計算ではあらかじめデータを用意するのではなく、数式で得られたデータをプロットしたいことが多いです。そのためこの記事では関数で計算された値をプロットする方法を習得することを目標とします。

動作環境と注意点

この記事では以下の環境で動作を確認しています。

                                                            
Mac OSmacOS Ventura 13.2.1
CPU1.4[GHz]
メモリ8[GB]
Xcode Version14.3(14E222b)
Swift Version5.8(swiftlang-5.8.0.124.2 clang-1403.0.22.11.100)
iPhone SE2 OSiOS 16.3.1

Xcode関連のバグ?Chartsで発生したエラーと解決例

ここで1つ注意点があります。筆者は最初にXcode14.3をインストールして使っていましたが、Chartsの公式サンプルコードを書いても以下のエラーが出て動作しませんでした(Chart{}の部分に)。

Cannot call value of non-function type 'module<Chart>

色々模索した結果、最終的にはXcodeのバージョンを一度14.1まで落として解決しました。Xcodeは複数のバージョンを共存させてインストールできます。

複数バージョンのXcodeを共存させた例
複数バージョンのXcodeを共存させた例

YouTubeやブログ記事で普通に使っている人がいたのでおかしいなと思いましたが、それらのコンテンツの作成日がXcode14.3になる前だったので試してみました!

そして一度解決したら上に記載している14.3でもエラーが出なくなりました。そのため現在は14.3でプログラミングをしています。

おそらく以下のエラーと同じ種類だと思うのですが、正直いまだに原因はわかりません。

Swift UI AsyncImage error - Cannot call value of non-function type 'module'

I am learning SwiftUI, from scratch and trying to show a Image from a URL, I am using the built in AsyncImage method, yet I get this error with no context. Cannot call value of non-function type 'module', Googling didn't help and I am following the exact same code line-by-line from a article I am following.

https://stackoverflow.com/questions/68770717/swift-ui-asyncimage-error-cannot-call-value-of-non-function-type-moduleasync

SwiftUIのChartsで散布図を作成するコード

まずは1点のみをプロットする

段階的にコードの書き方を確認しながら進めましょう。まずは以下の方針で1点だけポイントを表示させる散布図を作成します。

  • ボタンクリックで散布図の値が更新される
    Swiftで書くのは毎回GUIアプリです。今回もイベントと連動するということを確認します。Buttonウィジェットを追加し、イベントが書けるようにしておきます。ボタンイベントは「SwiftでiOSデバイスのマイクを使って録音機能を追加する方法」で書き方を紹介していますので参考にしてください。
  • データ変数を更新できるように宣言する
    ボタンイベントと関連し、散布図に渡すデータ変数(x値とy値)は@Stateを使って更新を監視できるように宣言します。@Stateについては「Swift入門:最低限覚えておく基礎文法の備忘録」に詳細を書きましたので、まだ読んでいない人は是非参考にしてください。
  • 関数を使ってデータの受け渡しを行う
    技術プログラミングでよく使う関数を介したデータのやり取りを想定します。関数はfuncで書きます。関数funcの書き方も「Swift入門:最低限覚えておく基礎文法の備忘録」に記載していますのでこちらも参考にしてください。

以下のコードをそのままXcodeのContentView.swiftに貼り付けることで、上記方針の通りに1点散布図プログラムが動きます。

こちらが実行結果です。Buttonをクリックする度にxとyが+1されて斜めに移動します。イベント処理と関数処理、値の更新処理がうまくいっていることを確認しました。グラフエリアのグリッドや文字は自動でフォーマットが決まっています。フォーマットを自由にいじるのは別途記事を書こうと思います。

1点散布図のSwiftコード実行結果

複数点をプロットしてみる

ボタンイベントなしのシンプル構成

1点はイメージしやすかったので次は複数点のプロットに挑戦します。基本的なコードは以下のサンプルを参考にしました。
https://developer.apple.com/documentation/charts/creating-a-chart-using-swift-charts

Pythonに慣れた筆者からすると、SwiftUIとChartを使った複数点の散布図プロットは少々面倒でした。そのためまずは公式のサンプルコードを参考に、ボタンイベントなしで作成します。

以下のコードはstructでx値とy値をそれぞれxValueとyValueで定義するPointsData構造体を作成し、dataで実際の値を定数として与えています。

このコードを実行するとこちらのように3点がプロットされます。これはまだまだサンプル通りなので序の口です。

3点散布図のプロット結果

ボタンイベントありの場合(関数演算付き)

要領を得たので次はボタンで値が更新されるイベントを追加しましょう。これはこれまで学んだことの組み合わせなのであと少し編集するだけです。せっかくなので横軸xに対して縦軸はランダムに値が決まるように.random()を使ってみます。

少し編集項目が多くなるので以下に箇条書きで説明を行った後にコードを示します。

  • var dataを動的に指定できるよう初期化する
    先ほどは「var data」にPointsDataの型を引き継いで直接データ点数を指定していましたが、どんなデータ長の数値配列が来ても対応できるように初期化の段階では空にしておきます
    そしてContentsView内で@Stateを使って宣言することで状態を監視し、更新が反映できるようにしてみます。
  • 関数で配列を作成する
    この記事の目標である関数でデータを用意する部分は「func wavePoints」で行っています。.randomを使っていたりしますが、基本は「Swift入門:最低限覚えておく基礎文法の備忘録」の記事でやっていたことと同じ内容です。
  • ボタンイベント
    Buttonをクリックすると先ほど定義した関数によるx配列(xArray)とy配列(randArray)がタプルで返ってきます。これを分解して散布図用のデータ形式にするわけですが、ここで.appendを使ってみました。.appendを使うことで動的にデータを構築できます。ただし、.appendの前に.removeAllで一度配列をクリアにしておかないとどんどんデータが追加されていってしまいます。

上記の内容に注意してコーディングすることでChart{}内のコードは何も変更することなく複数点のデータ更新やプロットが実装できます。
以下に全コードを示します。

以下に実行例を示します。ボタンクリックでランダムに散布図が描かれているので目標達成です。

複数点ボタンイベント更新のコード実行結果(散布図)

SwiftUIのChartsで折れ線グラフを作成するコード

次は折れ線グラフを作ってみます。おおげさにタイトルを付けてしまいましたが、先ほどのコードにおいてChart{}内のPointMarkをLineMarkに変更するだけでOKです。

こちらが実行結果です。ここまでできればグラフの種類変更も朝飯前のようです。

複数点ボタンイベント更新のコード実行結果(折れ線グラフ)

まとめ

本記事ではSwiftUIに追加されたChartsを使って散布図折れ線グラフを描くコードを紹介しました。structでデータ構造を決定し、実際のデータを作成する方法として静的、動的の2種類を書いてみました。Swiftを始めたばかりですが、ようやく構造体の使い方がわかってきた気がします。

基本は公式のサンプルコードを参考にすればある程度書けるといっても、Swiftでまともなコードを書いたことがない筆者にとってはエラー1つを解決するのに長い時間がかかってしまいました。

しかし少しずつ検証していくことでやりたいことに近づいているという感覚が持てたので、徐々にスピードアップすることができそうです。

Chartsは2022年後半に標準となったライブラリであり、さすがのChatGPT-4もあまり役に立ちませんでした。新技術に対するキャッチアップに必要な体力はやはり必要ですね…。

そのうちグラフの書式変更もやってみます。

ようやくSwiftUIを使ってグラフを描けるようになりました!
Twitterでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!

  • このエントリーをはてなブックマークに追加

SNSでもご購読できます。

コメントを残す

*