はじめに
WWDCでは、毎年Appleが提供する技術に関して、大量のアップデートが発表されます。その中から本記事では下記セクションに従って、それぞれのアップデート内容を解説していきます。
- 簡潔な表現で美しいグラフを表示できるフレームワーク、Charts
- SwiftUIとの連携強化や新コンポーネント追加によって、より洗練されたUIKit
対象読者
本記事の対象読者としては、WWDC2022で発表された技術について主要な内容のみをキャッチアップしたい方を想定しています。細かいアップデート内容や各トピックの詳細については、WWDC本編をご覧ください。
簡潔な表現で美しいグラフを表示できるフレームワーク、Charts
本セクションでは、新たに追加されたフレームワークであるChartsについての解説を行います。Chartsとは何かといったところから、基本的な実装方法の紹介までを行います。
Chartsとは
Chartsフレームワークはグラフを描画するためのフレームワークです。SwiftUIと同様に宣言的なシンタックスを用いてアプリ上にグラフを表示できるようになります。線グラフ・棒グラフといった基本的なグラフに加えてさまざまな表示パターンにも対応しており、あらゆるAppleプラットフォームで美しいUIを提供することが可能になります。
基本的な実装方法
Chartsでのグラフ実装はとてもシンプルです。棒グラフを表示するサンプルコードを通して実装方法を見ていきましょう。
サンプルではおにぎりの売上グラフを作成します。事前準備としてRiceBallのstructを定義し、名前と価格を保持できるようにしました。
struct RiceBall: Identifiable { let name: String let price: Int var id: String { name } }
ここで定義したnameはグラフの横軸の値として利用し、priceはグラフの縦軸の値として参照します。
次にグラフの表示を行います。グラフを表示するにはまずChartコンポーネントを宣言し、その内部でグラフの種類に応じたMarkの宣言をしていきます。棒グラフを表示したい場合はBarMarkと呼ばれるコンポーネントを利用することになるので、そのインスタンスを作成し、X軸、Y軸に表示すべき値をイニシャライザで受け渡すようにします。
struct ChartsSampleView: View { var body: some View { VStack(alignment: .leading) { Text("おにぎりの売上") Chart { let riceBall = RiceBall(name: "梅", price: 100) BarMark( x: .value("名前", riceBall.name), y: .value("売上", riceBall.price) ) } } } }
上記のサンプルコードを実行した場合、アプリの表示は以下のようになります。
このように、Chartsではたった数行のコードでグラフをUIとして表示することが可能になっています。
複数の値の表示
ここまでのサンプルコードではデータを1件表示しているだけでしたが、複数データのグラフはどうなるでしょうか。
準備として、まずは各おにぎりの販売履歴を表す配列を定義します。
let riceBalls: [RiceBall] = [ RiceBall(name: "梅", price: 100), RiceBall(name: "鮭", price: 120), RiceBall(name: "明太子", price: 150), RiceBall(name: "鮭", price: 120), RiceBall(name: "鮭", price: 120), RiceBall(name: "梅", price: 100), ]
これを基にして、種類別の合計売上金額を示すグラフを作ってみましょう。実装としてはSwiftUIのListに近い形で行い、ChartのイニシャライザかChart配下のForEachに配列を渡すことで実現します。ここでは前者のパターンで実装を行いました。
Chart(riceBalls) { riceBall in BarMark( x: .value("名前", riceBall.name), y: .value("売上", riceBall.price) ) }
アプリの表示は以下になります。
このように、売上の集計処理に関しては実装を全くしていないのにも関わらず、表示としては集計された値がy軸の値として用いられています。これは、Chartsがx軸に指定された値と同一の値を内部的に集計してくれているためで、実装者はその部分について明示的な計算処理をする必要もありません。
また、x軸に渡す値とy軸に渡す値を入れ替えることで、垂直方向に伸びたグラフを水平方向に伸びたグラフに変更することも簡単にできます。
時系列データへの対応
Chartsは時系列データの表示も簡単です。先ほどのRiceBallにsaleDateというプロパティを追加し、これをX軸の値として扱うことで時間ごとの販売金額を可視化できるようにしてみましょう。
[ RiceBall(name: "梅", price: 100, saleDate: .init(year: 2022, month: 09, day: 14, hour: 11)), RiceBall(name: "鮭", price: 120, saleDate: .init(year: 2022, month: 09, day: 14, hour: 12)), RiceBall(name: "明太子", price: 150, saleDate: .init(year: 2022, month: 09, day: 14, hour: 12)), RiceBall(name: "鮭", price: 120, saleDate: .init(year: 2022, month: 09, day: 14, hour: 12)), RiceBall(name: "鮭", price: 120, saleDate: .init(year: 2022, month: 09, day: 14, hour: 13)), RiceBall(name: "梅", price: 100, saleDate: .init(year: 2022, month: 09, day: 14, hour: 13)), ]
データ型を変更した後、Chart側の変更はほんのわずかです。
Chart(riceBalls) { riceBall in BarMark( x: .value("販売日", riceBall.saleDate, unit: .hour), // 変更箇所 y: .value("売上", riceBall.price) ) }
このように、nameの値を受け渡していた部分を、新たに追加したsaleDateを参照するようにしただけで最低限の実装は完了します。また、単位を特定のもので固定したい場合は、unit引数に目的の値を指定することで対応ができます。表示としては下記のように変わります。
リアクティブな変更
ChartsはSwiftUIと同様に状態の監視を自動で行います。そのため、値に変更があった場合にグラフを更新するといった挙動も自動で行うことが可能です。具体的には、RiceBallの配列をStateプロパティとして保持しておき、任意のタイミングで更新してあげるだけで実現できます。
@State private var riceBalls: [RiceBall] = [ /** ... **/ ] var body: some View { // ... Button("追加") { riceBalls.append(RiceBall(name: "ツナマヨ", price: 110)) } }
また、値の変更だけでなくMark自体の変更なども簡単に行えます。そういった機能を実装することで、よりリッチなグラフを提供することもできるでしょう。
Markによる表示形式の変更
グラフの表示形式はMarkと呼ばれるもので制御されます。Markはグラフの各表示形式ごとに提供されており、Propertyと組み合わせることで見た目を細かくコントロールできます。
先ほどまでのサンプルでは「BarMark」と呼ばれる棒グラフを扱うMarkを利用していました。他にも、現時点では以下のようなMarkがサポートされているので、用途に応じて適切な表示を実現することが可能となっています。
- AreaMark
- LineMark
- PointMark
- RectangleMark
- RuleMark
- BarMark
同じグラフ上に複数のMarkをプロットすることも可能なので、柔軟な対応が取れるようにもなっています。それぞれの詳細な使い方は公式ドキュメントに細かく記述されているので、そちらもご参考ください。
グラフのカスタマイズ
Chartsはデータを受け渡すだけで最適な表示を自動で行ってくれます。一方、提供するアプリの要件によっては特定のユースケースに最適化した表示を行いたい要望も出てくるでしょう。Chartsはそういったケースにも対応可能です。
例えば、先ほど作成したおにぎりの売上グラフに対して、ラベルのカスタマイズを行ってみましょう。X軸の表示には絵文字を付与し、Y軸の表示を消してみます。
Chart(riceBalls) { riceBall in BarMark( x: .value("名前", riceBall.name), y: .value("売上", riceBall.price) ) }.chartXAxis { AxisMarks { value in let name = value.as(String.self) ?? "" AxisValueLabel(centered: true) { Text("\(name)🍙") } } }.chartYAxis(.hidden)
このようにchartXAxisModifierやchartYAxisModifierを活用することで、より細かい表示の設定も実現可能となっています。
Swift Chartsについてのまとめ
以上がSwift Chartsのご紹介でした。アプリ上にグラフを表示してより良いユーザー体験を提供するには、技術的な部分以外にもさまざまなプラクティスがあります。WWDC2022のセッションではそれらも含めて詳細な解説がされているので、実際に組み込む際には参考になるかと思います。