はじめに
SwiftUIでグラフを作成するにはChartsを使うと便利なのは皆さんご存知かと思います。
しかし、要件に合うような細かな仕様を実現するには一工夫が必要です。
私の場合、折れ線グラフの色を条件によって描画の途中で変えたい需要がありました。その実装例を紹介します。
実装方法
ここでは例として、取得した値が500以上なら青、250以上500未満なら緑、250未満なら赤にグラフの色を変えてみます。
先にネタばらしすると、色の異なる3本のグラフ線を同時に走らせ、条件に合う1本のみが値を持つようにし、他2本を0にします。
GraphView.swift
//グラフ描画用データ構造体
struct DataPoint: Identifiable {
let id = UUID()
let index: Int // 横軸(時間)
let data1: Int // 500以上
let data2: Int // 250以上500未満
let data3: Int // 250未満
}
// ラベル
enum GraphLabel: String {
case time = "時間"
case data1 = "500以上"
case data2 = "250以上500未満"
case data3 = "250未満"
}
// 新しい値を受け取る
class Graph: ObservableObject {
@Published var points: [DataPoint] = [] // グラフデータ
// 新しい値の受信時にコール
func addValue(value: Int) {
let point : DataPoint
/* 500以上 */
if (value >= 500){
point = DataPoint(index: points.count , data1: value, data2: 0, data3: 0)
}
/* 250以上 */
else if (value >= 250){
point = DataPoint(index: points.count, data1: 0, data2: value, data3: 0)
}
/* 250未満 */
else{
point = DataPoint(index: points.count, data1: 0, data2: 0, data3: value)
}
points.append(point) // グラフ描画
}
}
// データ更新毎にコール
struct GraphView: View {
@ObservedObject var graph: Graph
Chart {
ForEach(Array(graph.points.suffix(250))) { element in
AreaMark(
x: .value(GraphLabel.time.rawValue, element.index),
y: .value(GraphLabel.data1.rawValue, element.data1)
)
.foregroundStyle(by: .value(GraphLabel.data1.rawValue,
GraphLabel.data1.rawValue))
.interpolationMethod(.stepCenter)
AreaMark(
x: .value(GraphLabel.time.rawValue, element.index),
y: .value(GraphLabel.data2.rawValue, element.data2)
)
.foregroundStyle(by: .value(GraphLabel.data2.rawValue,
GraphLabel.data2.rawValue))
.interpolationMethod(.stepCenter)
AreaMark(
x: .value(GraphLabel.time.rawValue, element.index),
y: .value(GraphLabel.data3.rawValue, element.data3)
)
.foregroundStyle(by: .value(GraphLabel.data3.rawValue,
GraphLabel.data3.rawValue))
.interpolationMethod(.stepCenter)
}
}
.chartForegroundStyleScale([
GraphLabel.data1.rawValue: .blue.opacity(0.5), // 青
GraphLabel.data2.rawValue: .green.opacity(0.5), // 緑
GraphLabel.data3.rawValue: .red.opacity(0.5), // 赤
])
}
実際の動作
ポイント1:AreaMarkを使う
線だけだとグラフが切り替わる際に生じる縦線が表示されてしまいます。これを塗りつぶしのグラフにすることで上手く誤魔化してます。
ポイント2:.interpolationMethod(.stepCenter)を使う
これも重要です。滑らかなグラフだとグラフが切り替わる瞬間をユーザーに悟られてしまいます。わざとデジタルチックなグラフにすることであたかも瞬時に色が変わったように見せます。
最後に
SwiftUIを使うと圧倒的に早くコーディングできますが、自由度に欠ける部分があります。工夫を凝らして十分に妥協できるUIを目指したいですね。
