在 iOS 16 的新版 SwiftUI 中,我最喜歡的其中一個功能就是 Charts 框架。在 iOS 16 之前,我們需要構建自己的圖表、或是依靠第三方程式庫來建立圖表。Apple 推出了這個新框架,開發者就可以更輕鬆地創建動畫化和互動的圖表。
我之前在這篇文章已經簡單介紹過 Charts API 的用法。在這篇文章中,讓我們來看看如何用 Charts API 來構建折線圖吧!
天氣數據
我們會示範使用 SwiftUI Charts API,來構建一個折線圖,顯示 2021 年 7 月至 2022 年 6 月香港、台北和倫敦的平均氣溫。
讓我們先建立一個 WeatherData 結構,來儲存天氣數據。
struct WeatherData: Identifiable {
let id = UUID()
let date: Date
let temperature: Double
init(year: Int, month: Int, day: Int, temperature: Double) {
self.date = Calendar.current.date(from: .init(year: year, month: month, day: day)) ?? Date()
self.temperature = temperature
}
}因為 Chart initializer 接受 Identifiable 物件的列表,所以我們要讓 WeatherData 遵從 Identifiable 協定。
我們要為每個城市創建一個陣列 (array),來儲存天氣數據。以下是倫敦的陣列:
let londonWeatherData = [ WeatherData(year: 2021, month: 7, day: 1, temperature: 19.0),
WeatherData(year: 2021, month: 8, day: 1, temperature: 17.0),
WeatherData(year: 2021, month: 9, day: 1, temperature: 17.0),
WeatherData(year: 2021, month: 10, day: 1, temperature: 13.0),
WeatherData(year: 2021, month: 11, day: 1, temperature: 8.0),
WeatherData(year: 2021, month: 12, day: 1, temperature: 8.0),
WeatherData(year: 2022, month: 1, day: 1, temperature: 5.0),
WeatherData(year: 2022, month: 2, day: 1, temperature: 8.0),
WeatherData(year: 2022, month: 3, day: 1, temperature: 9.0),
WeatherData(year: 2022, month: 4, day: 1, temperature: 11.0),
WeatherData(year: 2022, month: 5, day: 1, temperature: 15.0),
WeatherData(year: 2022, month: 6, day: 1, temperature: 18.0)
]另外,我們也有一個陣列去儲存三個城市的天氣數據:
let chartData = [ (city: "Hong Kong", data: hkWeatherData),
(city: "London", data: londonWeatherData),
(city: "Taipei", data: taipeiWeatherData) ]利用 Chart 和 LineMark 來建立一個簡單的折線圖
不論我們要利用 Charts 框架建立什麼圖表,都需要先匯入 Charts 框架:
import Charts然後,我們就可以從 Chart 視圖開始。在 Chart 視圖內,讓我們提供一組 LineMark 來構建折線圖:
以上的程式碼繪製了一個折線圖,來顯示香港的平均氣溫。ForEach 語句 loop through 儲存在 hkWeatherData 中的所有項目。我們會為每個項目創建一個 LineMark 物件,當中 x 軸設置為日期,而 y 軸則設置為平均氣溫。
我們也可以選擇使用 frame 修飾符,來調整圖表的大小。如果我們在 Xcode 預覽中預覽程式碼,應該會看到以下折線圖:

客製化圖表軸
我們可以利用 chartXAxis 和 chartYAxis 修飾符,來客製化 x 和 y 軸。比如說,如果我們想以數字格式顯示月份,我們可以將 chartXAxis 修飾符附加到 Chart 視圖:
.chartXAxis {
AxisMarks(values: .stride(by: .month)) { value in
AxisGridLine()
AxisValueLabel(format: .dateTime.month(.defaultDigits))
}
}在 chartXAxis 中,我們為月份的數值創建了一個 AxisMarks 的視覺標記 (visual mark)。針對每個數值,我們可以使用特定格式顯示一個 ValueLabel。以下這行程式碼就告訴了 SwiftUI 圖表,我們想要使用數字格式顯示月份:
.dateTime.month(.defaultDigits)另外,我們也使用了 AxisGridLine 來添加一些 grid line。
至於 y 軸,我們之前是在後面(右側)顯示 y 軸的,我們想改為在前面(左側)顯示。讓我們如此附加 chartYAxis 修飾符:
做好改動之後,Xcode 預覽應該會把圖表更新如下。y 軸會在左側顯示,而月份的格式也會變成以數字格式顯示。另外,你也應該會看到 grid line。

客製化繪圖區域的背景顏色
我們可以利用 chartPlotStyle 修飾符,來更改繪圖區域的背景顏色。讓我們將修飾符附加到 Chart 視圖:
.chartPlotStyle { plotArea in
plotArea
.background(.blue.opacity(0.1))
}然後,我們就可以使用 background 修飾符更改繪圖區域的顏色。在上面的例子中,我們把繪圖區域更改成淺藍色。

構建多於一條線的折線圖
現在,圖表只顯示單一數據(香港的天氣數據),那我們如何把倫敦和台北的天氣數據顯示在同一個折線圖中呢?
我們可以這樣重寫 Chart 視圖的程式碼:
Chart {
ForEach(chartData, id: \.city) { series in
ForEach(series.data) { item in
LineMark(
x: .value("Month", item.date),
y: .value("Temp", item.temperature)
)
}
.foregroundStyle(by: .value("City", series.city))
}
}我們有另一個 ForEach 來 loop through 三個城市的數據。我們在這裡使用了 foregroundStyle 修飾符,為每條線應用不同的顏色。我們不需要指定顏色,SwiftUI 會自動選擇顏色。

現在,三個城市的符號都相同。如果要使用不同的符號,讓我們在 foregroundStyle 之後添加這行程式碼:
.symbol(by: .value("City", series.city))如此一來,不同城市就會有不同的符號了。

客製化內插 (Interpolation) 方法
我們可以把 interpolationMethod 修飾符附加到 LineMark,來更改折線圖的內插方法。
.interpolationMethod(.stepStart)如果我們把內插方法設置為 .stepStart,折線圖就會變成這樣:

除了 .stepStart 之外,我們還可以使用以下設定:
- cardinal
- catmullRom
- linear
- monotone
- stepCenter
- stepEnd
總結
Charts 框架是 SwiftUI 一個很好的新功能,即使是 SwiftUI 的初學者,用幾行程式碼,就可以構建出漂亮的圖表。雖然這篇教學文章以折線圖為例子,但其實我們可以利用 Charts API 輕鬆地將折線圖轉換為其他圖表,例如長條圖。你可以參閱 Swift Charts 文檔深入了解這個 API。
備註:我們正就著 iOS 16 更新《精通 SwiftUI》一書。如你有意學習 SwiftUI,歡迎透過網頁購買書籍,我們會在今年免費為大家更新本書。