概要
iOS16から使用できるようになったChartsフレームワークについて使用方法と大まかな概要を説明した記事になります。
はじめに
Chartとグラフが混同して出てきますが、コードに関する部分ではChartと表現し、図として表現したい場合はグラフとしています。
Chart
まずはドキュメントを参考にした簡単な棒グラフのサンプルコードとプレビューです。
import SwiftUI import Charts struct ChartsSampleView: View { var data: [ToyShape] = [ .init(type: "Cube", count: 5), .init(type: "Sphere", count: 4), .init(type: "Pyramid", count: 4) ] var body: some View { Chart { BarMark( x: .value("Shape Type", data[0].type), y: .value("Total Count", data[0].count) ) BarMark( x: .value("Shape Type", data[1].type), y: .value("Total Count", data[1].count) ) BarMark( x: .value("Shape Type", data[2].type), y: .value("Total Count", data[2].count) ) } } } struct ChartsSampleView_Previews: PreviewProvider { static var previews: some View { ChartsSampleView() } } struct ToyShape: Identifiable { var type: String var count: Double var id = UUID() }
Chartを利用するにはChartsライブラリのImportが必要です。
使用方法はChart内にMarkを使用することでChartを表示することができます。
上記のサンプルではBarMarkを使用して棒グラフを表示していますが、棒グラフ一つ毎にBarMarkの呼び出しが必要となります。
またx: .value()には横軸にStringを、y: .value( )には縦軸でDoubleの値を入れることで縦棒グラフができます。(横棒グラフに関してはBarMarkのセクションで説明します)
基本的なChartの使用に関してはこれだけで、簡単にグラフを作成することができます。
MarkとProperty
このBarMarkについては各項目の視覚的要素の一種であり、それらの視覚要素を総称してMarkと呼びます。
Markについては6種類ありますが、そちらに関しては後ほど説明します。
またそれぞれのMarkに対して使用できるPropertyがあり、それらを効果的に使用することでより様々なグラフやChartを作成することができます。
上記サンプルでxやyが出てきましたがこれらはX PositionとY Positionにあたります。
foregroundStyle
foregroundStyleを使用すると棒に色をつけることができます。
ただ色の指定はforegroundStyleではできないのでchartForegroundStyleScaleで定義する必要があります。
以下積み上げ棒グラフのサンプルコードを参考にforegroundStyleを使用したサンプルです。
import SwiftUI import Charts struct ChartsSampleView: View { var stackedBarData: [ToyShape] = [ .init(color: "Green", type: "Cube", count: 2), .init(color: "Green", type: "Sphere", count: 0), .init(color: "Green", type: "Pyramid", count: 1), .init(color: "Purple", type: "Cube", count: 1), .init(color: "Purple", type: "Sphere", count: 1), .init(color: "Purple", type: "Pyramid", count: 1), .init(color: "Pink", type: "Cube", count: 1), .init(color: "Pink", type: "Sphere", count: 2), .init(color: "Pink", type: "Pyramid", count: 0), .init(color: "Yellow", type: "Cube", count: 1), .init(color: "Yellow", type: "Sphere", count: 1), .init(color: "Yellow", type: "Pyramid", count: 2) ] var body: some View { Chart { ForEach(stackedBarData) { shape in BarMark( x: .value("Shape Type", shape.type), y: .value("Total Count", shape.count) ) .foregroundStyle(by: .value("Shape Color", shape.color)) } } .chartForegroundStyleScale ([ "Green": .green, "Purple": .purple, "Pink": .pink, "Yellow": .yellow ]) .padding() } } struct ChartsSampleView_Previews: PreviewProvider { static var previews: some View { ChartsSampleView() } } struct ToyShape: Identifiable { var color: String var type: String var count: Double var id = UUID() }
stackedBarDataのようにデータの数が増えてくるとChart内で定義するBarMarkの数も比例して多くなります。
そこでForEachを使用して繰り返し処理を行います。
余談ですがForEachでstructを使用するにはIdentifiableに準拠させる必要があります。
またcolorを追加することで凡例が追加されます。
ForEachを使わず、Chartに直接引数としてstackedBarDataを渡して、クロージャーの引数としてToyShape型を定義することもできます。
Chart(stackedBarData) { shape in BarMark( x: .value("Shape Type", shape.type), y: .value("Total Count", shape.count) ) .foregroundStyle (by: .value("Shape Color", shape.color)) }
annotation
ChartにTextやImageを注釈としてつけることができます。
Chart(stackedBarData) { shape in BarMark( x: .value("Shape Type", shape.type), y: .value("Total Count", shape.count) ) .foregroundStyle (by: .value("Shape Color", shape.color)) .annotation(position: .top) { shape.type == "Cube" ? Text("★") : Text("☆") } }
annotationの引数にpositionを指定することで配置を変えることもできます。
SymbolやLine Styleに関しては以下の「Markの種類」セクション内で説明します。
Markの種類
Markの種類はBarを含めて以下6種類あります。
- AreaMark
- LineMark
- PointMark
- RectangleMark
- RuleMark
- BarMark
以下それぞれの使用方法と特徴です。
※BarMark以外の各Markにて共通して使用するサンプルデータは以下の通りです。
struct SampleData: Identifiable { var id: String { name } let name: String let amount: Double let from: String } let sampleData: [SampleData] = [ .init(name: "NameA", amount: 2500, from: "PlaceA"), .init(name: "NameB", amount: 3500, from: "PlaceA"), .init(name: "NameC", amount: 2000, from: "PlaceA"), .init(name: "NameD", amount: 4500, from: "PlaceA"), .init(name: "NameE", amount: 5000,from: "PlaceA"), .init(name: "NameF", amount: 5500,from: "PlaceA"), .init(name: "NameA", amount: 360, from: "PlaceB"), .init(name: "NameB", amount: 640, from: "PlaceB"), .init(name: "NameC", amount: 680, from: "PlaceB"), .init(name: "NameD", amount: 760, from: "PlaceB"), .init(name: "NameE", amount: 780, from: "PlaceB"), .init(name: "NameF", amount: 800, from: "PlaceB") ]
AreaMark
面グラフを作成することができます。
以下サンプルコードとプレビューです。
struct AreaMarkView: View { var body: some View { Chart(sampleData) { data in AreaMark( x: .value("Name", data.name), y: .value("Amount", data.amount) ) .foregroundStyle(by: .value("From", data.from)) .position(by: .value("From", data.from)) } .chartForegroundStyleScale([ "PlaceA": .green.opacity(0.4), "PlaceB": .purple.opacity(0.4) ]) .frame(height: 300) .padding() } }
RuleMark
RuleMarkに関しては少し特殊で、単体で使用してグラフを表示するものではなく、単一の水平線または垂直線を表示します。
以下サンプルコードとプレビューです。
struct RuleMarkView: View { var body: some View { Chart(sampleData) { data in AreaMark( x: .value("Name", data.name), y: .value("Amount", data.amount) ) .foregroundStyle(by: .value("From", data.from)) .position(by: .value("From", data.from)) RuleMark(y: .value("Base", 2500)) .lineStyle(StrokeStyle(lineWidth: 2)) .annotation(position: .top, alignment: .leading) { Text("Base Line 2500") .font(.subheadline) .foregroundColor(.blue) } } .chartForegroundStyleScale([ "PlaceA": .green.opacity(0.4), "PlaceB": .purple.opacity(0.4) ]) .frame(height: 300) .padding() } }
annotationを使用すると基準となるラインなどに使用できます。
LineMark
折れ線グラフを作成することができます。
以下サンプルコードとプレビューです。
struct LineMarkView: View { var body: some View { Chart(sampleData) { data in LineMark( x: .value("Name", data.name), y: .value("Amount", data.amount) ) .foregroundStyle(by: .value("Form", data.from)) .lineStyle(StrokeStyle(lineWidth: 1)) } .frame(height: 300) .padding() } }
lineStyle(StrokeStyle())で線の太さなどを変化させたり.interpolationMethod()で滑らかな線や直線的な線に変更することができます。
symbol()を使用すると各値にマーカーのようなシンボルを表示することができます。
struct LineMarkView: View { var body: some View { Chart(sampleData) { data in LineMark( x: .value("Name", data.name), y: .value("Amount", data.amount) ) .foregroundStyle(by: .value("Name", data.from)) .lineStyle(StrokeStyle(lineWidth: 1)) .interpolationMethod(.linear) .symbol(by: .value("Form", data.from)) } .frame(height: 300) .padding() } }
PointMark
点グラフを作成することができます。
以下サンプルコードとプレビューです。
struct RectangleMarkView: View { var body: some View { Chart(sampleData) { data in PointMark( x: .value("Name", data.name), y: .value("Amount", data.amount) ) .foregroundStyle(by: .value("Form", data.from)) } .frame(height: 300) .padding() } }
LineMarkと併用して使用することでsymbolのように値にマーカーとして表示することも可能です。
struct PointMarkView: View { var body: some View { Chart(sampleData) { data in LineMark( x: .value("Name", data.name), y: .value("Amount", data.amount) ) .foregroundStyle(by: .value("Form", data.from)) .lineStyle(StrokeStyle(lineWidth: 3)) .interpolationMethod(.linear) PointMark( x: .value("Name", data.name), y: .value("Amount", data.amount) ) .foregroundStyle(by: .value("Form", data.from)) } .chartForegroundStyleScale([ "PlaceA": .green.opacity(0.4), "PlaceB": .purple.opacity(0.4) ]) .frame(height: 300) .padding() } }
RectangleMark
線表を作成することができます。
以下サンプルコードとプレビューです。
struct RectangleMarkView: View { var body: some View { Chart(sampleData) { data in RectangleMark( x: .value("Name", data.name), y: .value("Amount", data.amount) ) .foregroundStyle(by: .value("Form", data.from)) } .frame(height: 300) .padding() } }
BarMark
棒グラフを作成することができます。
以下サンプルコードとプレビューです。
struct BarMarkView: View { let barColor: [Color] = [.red, .purple, .blue, .green, .yellow] var body: some View { Chart(productData) { data in BarMark( x: .value("ProductName", data.name), y: .value("Count", data.count) ) .foregroundStyle(by: .value("ProductName", data.name)) } .chartForegroundStyleScale( domain: productData.compactMap({ product -> String? in product.name }), range: barColor ) .frame(height: 300) .padding() } } struct BarMarkView_Previews: PreviewProvider { static var previews: some View { BarMarkView() } } struct Products: Identifiable { var id: String { name } let name: String let count: Double } let productData: [Products] = [ .init(name: "製品A", count: 960), .init(name: "製品B", count: 420), .init(name: "製品C", count: 660), .init(name: "製品D", count: 540), .init(name: "製品E", count: 1120) ]
XとYの値を反転させると横棒グラフになります。
BarMark( x: .value("Count", data.count), y: .value("ProductName", data.name) )
幅を指定することもできます。
BarMark( x: .value("Shape Type", data[0].type), y: .value("Total Count", data[0].count), width: 10 )
また積み上げ棒グラフではなく同じ項目内で比較したいグラフを表示したい時は.positionを使用します。
struct BarMarkView: View { var body: some View { Chart(productData) { data in BarMark( x: .value("Count", data.count), y: .value("ProductName", data.name), ) .foregroundStyle(by: .value("ProductName", data.factory)) .position(by: .value("Factory", data.factory)) } .chartForegroundStyleScale([ "工場A": .green, "工場B": .purple ]) .frame(height: 300) .padding() } } struct BarMarkView_Previews: PreviewProvider { static var previews: some View { BarMarkView() } } struct Products: Identifiable { var id: String { name } let name: String let count: Double let factory: String } let productData: [Products] = [ .init(name: "製品A", count: 960, factory: "工場A"), .init(name: "製品B", count: 420, factory: "工場A"), .init(name: "製品C", count: 660, factory: "工場A"), .init(name: "製品D", count: 540, factory: "工場A"), .init(name: "製品E", count: 1120, factory: "工場A"), .init(name: "製品A", count: 360, factory: "工場B"), .init(name: "製品B", count: 640, factory: "工場B"), .init(name: "製品C", count: 780, factory: "工場B"), .init(name: "製品D", count: 960, factory: "工場B"), .init(name: "製品E", count: 880, factory: "工場B") ]
最後に
ざっとChartについての説明からかくMarkについてサンプルコードとプレビューで紹介させていただきました。
次回はもっと複雑な使用方法について掘り下げたいと思います。
参考記事
https://developer.apple.com/documentation/charts/creating-a-chart-using-swift-charts
https://developer.apple.com/documentation/charts/barmark
https://developer.apple.com/documentation/charts/barmark/init(x:y:width:height:stacking:)