在 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,歡迎透過網頁購買書籍,我們會在今年免費為大家更新本書。