Xcode 14 提供的新 Charts 框架在將多個時間序列資料同時顯示在圖表上的方式既有創意也很簡單,首先定義兩個結構用來存放原始資料,先以兩個溫度感測器收集的溫度資料做為範例,這樣應該比較有感覺。
// 每筆記錄只存 publishTime 與 value
struct Record: Identifiable {
var publishTime: String
var value: Int
var id = UUID()
var date: Date { // 將時間日期字串轉成標準的 Date
get {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy/M/d H:m:s"
return formatter.date(from: publishTime)!
}
}
}
// 每個感測器所儲存的時間序列資料
struct TemperatureSensor: Identifiable {
var id: String
var records: [Record]
}
接下來填入兩個溫度感測器的資料,一個是放在車庫的溫度計,另一個是在花園的溫度計。當然實際應用時,這裡的資料應該是從資料庫讀出來,但這裡就模擬一下。
let allSensors: [TemperatureSensor] = [
.init(id: "車庫", records: [
.init(publishTime: "2022-6-1 10:0:0", value: 23),
.init(publishTime: "2022-6-1 11:0:0", value: 24),
.init(publishTime: "2022-6-1 12:0:0", value: 26),
.init(publishTime: "2022-6-1 13:0:0", value: 29),
.init(publishTime: "2022-6-1 14:0:0", value: 28),
.init(publishTime: "2022-6-1 15:0:0", value: 25),
]),
.init(id: "花園", records: [
.init(publishTime: "2022-6-1 10:0:0", value: 18),
.init(publishTime: "2022-6-1 11:0:0", value: 19),
.init(publishTime: "2022-6-1 12:0:0", value: 23),
.init(publishTime: "2022-6-1 13:0:0", value: 17),
.init(publishTime: "2022-6-1 14:0:0", value: 16),
.init(publishTime: "2022-6-1 15:0:0", value: 14),
])
]
接下來用折線圖(LineMark)把兩個地方的溫度資料畫出來就可以了。這裡有幾個有趣的地方,首先看到 x 軸後面有一個 unit 參數,這裡可以選擇圖表上時間序列資料的時間單位,目前自然是以小時為單位。另外,foregroundStyle 用來畫出多個獨立資料的圖,若沒有這個 modifier,兩個感測器的資料會連在一起畫。
struct TemperatureChart: View {
var body: some View {
Chart(allSensors) { sensor in
ForEach(sensor.records) { record in
LineMark(
x: .value("Time", record.date, unit: .hour),
y: .value("Value", record.value)
)
}
.foregroundStyle(by: .value("Sensor Location", sensor.id))
}
}
}
如果想要在資料點上加記號,只要使用 symbol 修飾器就可以。
struct TemperatureChart: View {
var body: some View {
Chart(allSensors) { sensor in
ForEach(sensor.records) { record in
LineMark(
x: .value("Time", record.date, unit: .hour),
y: .value("Value", record.value)
)
}
.foregroundStyle(by: .value("Sensor Location", sensor.id))
.symbol(by: .value("Sensor Location", sensor.id))
.symbolSize(200)
}
}
}
最後使用 RuleMark 加一條水平直線來顯示平均溫度,並透過修飾器來改變顏色與線條樣式。
RuleMark (y: .value("Average", 21.8))
.foregroundStyle(.purple)
.lineStyle(.init(lineWidth: 2, dash: [5, 3]))
最後呈現的結果像這個樣子。雖然目前的圖表種類不多,但架構有了,未來應該會有更多的圖表類型出現,期待圓餅圖、雷達圖…。
【延伸閱讀】