在 iOS 16 中用 SwiftUI Charts 建立一個折線圖
前言
蘋果在 WWDC 2022 上推出了 SwiftUI 圖表,這使得在 SwiftUI 檢視中建立圖表變得異常簡單。圖表是以豐富的格式呈現視覺化資料的一種很好的方式,而且易於理解。本文展示瞭如何用比以前從頭開始建立同樣的折線圖少得多的程式碼輕鬆建立折線圖。此外,自定義圖表的外觀和感覺以及使圖表中的資訊易於訪問也是非常容易的。
如以前的文章所示,不使用 SwiftUI Charts 也可以建立一個折線圖。然而,使用 Charts 框架可以提供大量的圖表來探索對應用程式中的資料最有效的方法,從而使它變得更加容易。
系列文章
- 如何在 SwiftUI 中建立條形圖
- SwiftUI 中的水平條形圖
- 在 iOS 16 中用 SwiftUI Charts 建立一個折線圖
- 在 iOS16 中用 SwiftUI 圖表定製一個線圖
- 在 Swift 圖表中使用 Foudation 庫中的測量型別
簡單折線圖
從包含一週的步數的資料開始,類似於 在SwiftUI中建立折線圖 中使用的資料。定義一個結構來儲存日期和該日的步數,併為當前周建立一個數組。
```swift struct StepCount: Identifiable { let id = UUID() let weekday: Date let steps: Int
init(day: String, steps: Int) {
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd"
self.weekday = formatter.date(from: day) ?? Date.distantPast
self.steps = steps
}
}
let currentWeek: [StepCount] = [ StepCount(day: "20220717", steps: 4200), StepCount(day: "20220718", steps: 15000), StepCount(day: "20220719", steps: 2800), StepCount(day: "20220720", steps: 10800), StepCount(day: "20220721", steps: 5300), StepCount(day: "20220722", steps: 10400), StepCount(day: "20220723", steps: 4000) ] ```
要建立一個折線圖,為步數資料中的每個元素建立一個帶有LineMark的圖表。在LineMark
的 X 值中指定工作日,在 Y 值中指定步數。注意,還需要匯入Charts
框架。
這就為步數資料建立了一個線形圖。由於只有一個系列的資料,ForEach
可以省略,資料可以直接傳遞給 Chart
初始化器。兩個部分都產生相同的折線圖。
```swift import SwiftUI import Charts
struct LineChart1: View { var body: some View { VStack { GroupBox ( "Line Chart - Step Count") { Chart { ForEach(currentWeek) { LineMark( x: .value("Week Day", $0.weekday, unit: .day), y: .value("Step Count", $0.steps) ) } } }
GroupBox ( "Line Chart - Step Count") {
Chart(currentWeek) {
LineMark(
x: .value("Week Day", $0.weekday, unit: .day),
y: .value("Step Count", $0.steps)
)
}
}
}
}
} ```
使用 SwiftUI Charts 建立的折線圖顯示每日步數
其他圖表
SwiftUI Charts 有許多可用的圖表選項。這些可以通過將圖表標記從LineMark
改為其他型別的標記(如BarMark
)來生成條形圖。
```swift struct OtherCharts: View { var body: some View { VStack { GroupBox ( "Line Chart - Step count") { Chart(currentWeek) { LineMark( x: .value("Week Day", $0.weekday, unit: .day), y: .value("Step Count", $0.steps) ) } }
GroupBox ( "Bar Chart - Step count") {
Chart(currentWeek) {
BarMark(
x: .value("Week Day", $0.weekday, unit: .day),
y: .value("Step Count", $0.steps)
)
}
}
GroupBox ( "Point Chart - Step count") {
Chart(currentWeek) {
PointMark(
x: .value("Week Day", $0.weekday, unit: .day),
y: .value("Step Count", $0.steps)
)
}
}
GroupBox ( "Rectangle Chart - Step count") {
Chart(currentWeek) {
RectangleMark(
x: .value("Week Day", $0.weekday, unit: .day),
y: .value("Step Count", $0.steps)
)
}
}
GroupBox ( "Area Chart - Step count") {
Chart(currentWeek) {
AreaMark(
x: .value("Week Day", $0.weekday, unit: .day),
y: .value("Step Count", $0.steps)
)
}
}
}
}
} ```
使用 SwiftUI 圖表建立的其他圖表型別,顯示每日步數
讓折線圖增加可訪問性
將圖表植入 SwiftUI 的一個好處是,可以很容易地使用 可訪問性修飾符 使圖表變得可訪問。為 StepCount 新增一個計算屬性,將資料返回為一個字串,可由 accessibilityLabel
使用。然後為圖表中的每個標記新增可訪問性標籤和值。
```swift struct StepCount: Identifiable { let id = UUID() let weekday: Date let steps: Int
init(day: String, steps: Int) {
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd"
self.weekday = formatter.date(from: day) ?? Date.distantPast
self.steps = steps
}
var weekdayString: String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyMMdd"
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .none
dateFormatter.locale = Locale(identifier: "en_US")
return dateFormatter.string(from: weekday)
}
} ```
swift
GroupBox ( "Line Chart - Daily Step Count") {
Chart(currentWeek) {
LineMark(
x: .value("Week Day", $0.weekday, unit: .day),
y: .value("Step Count", $0.steps)
)
.accessibilityLabel($0.weekdayString)
.accessibilityValue("\($0.steps) Steps")
}
}
在 SwiftUI 圖表中使折線圖可訪問性
為折線圖新增多個數據序列
折線圖是比較兩個不同系列資料的好方法。建立第二個系列,即前一週的步數,並將這兩個系列新增到折線圖中。
```swift let previousWeek: [StepCount] = [ StepCount(day: "20220710", steps: 15800), StepCount(day: "20220711", steps: 7300), StepCount(day: "20220712", steps: 8200), StepCount(day: "20220713", steps: 25600), StepCount(day: "20220714", steps: 16100), StepCount(day: "20220715", steps: 16500), StepCount(day: "20220716", steps: 3200) ]
let currentWeek: [StepCount] = [ StepCount(day: "20220717", steps: 4200), StepCount(day: "20220718", steps: 15000), StepCount(day: "20220719", steps: 2800), StepCount(day: "20220720", steps: 10800), StepCount(day: "20220721", steps: 5300), StepCount(day: "20220722", steps: 10400), StepCount(day: "20220723", steps: 4000) ]
let stepData = [ (period: "Current Week", data: currentWeek), (period: "Previous Week", data: previousWeek) ] ```
第一次嘗試新增這兩個系列的資料沒有按預期顯示。
swift
struct LineChart2: View {
var body: some View {
GroupBox ( "Line Chart - Daily Step Count") {
Chart {
ForEach(stepData, id: \.period) {
ForEach($0.data) {
LineMark(
x: .value("Week Day", $0.weekday, unit: .day),
y: .value("Step Count", $0.steps)
)
.accessibilityLabel($0.weekdayString)
.accessibilityValue("\($0.steps) Steps")
}
}
}
}
}
}
第一次嘗試在 SwiftUI Charts 中建立一個包含兩個系列步數資料的折線圖
顯示步數系列
在折線圖中顯示多個基於工作日的步數系列
最初嘗試在折線圖中顯示多組資料的問題是X軸使用了日期。當前的週數緊接著上一週,所以每一個點都是沿著X軸線性遞增繪製的。
有必要只用工作日作為X軸的數值,這樣所有的週日都在同一個X座標上繪製。
在StepCount
中新增另一個計算屬性,以便以字串格式返回工作日的短日。
```swift struct StepCount: Identifiable {
. . .
var shortDay: String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEE"
return dateFormatter.string(from: weekday)
}
} ```
此 shortDay
用於圖表中 LineMarks
的 x 值。另外,前景的樣式設定為基於stepCount
陣列的週期。折線圖使用 x 軸的工作日來顯示兩週的步數,以便在周之間進行比較。
```swift struct LineChart3: View { var body: some View { VStack { GroupBox ( "Line Chart - Daily Step Count") { Chart { ForEach(stepData, id: .period) { steps in ForEach(steps.data) { LineMark( x: .value("Week Day", $0.shortDay), y: .value("Step Count", $0.steps) ) .foregroundStyle(by: .value("Week", steps.period)) .accessibilityLabel($0.weekdayString) .accessibilityValue("($0.steps) Steps") } } } .frame(height:400) } .padding()
Spacer()
}
}
} ```
SwiftUI 圖表中帶有兩個系列的步數資料的折線圖
結論
在 SwiftUI Charts 中還有很多東西可以探索。使用這個框架顯然比從頭開始建立你自己的圖表要好。
本文正在參加「金石計劃 . 瓜分6萬現金大獎」
- 在 SwiftUI 中建立一個環形 Slider
- Swift 週報 第二十五期
- Swift 週報 第二十四期
- 在 iOS 16 中用 SwiftUI Charts 建立一個折線圖
- Swift 中的 async/await ——程式碼例項詳解
- Swift AsyncSequence — 程式碼例項詳解
- Swift 週報 第十期
- SwiftUI 之 HStack 和 VStack 的切換
- 第三方庫並不是必須的
- Swift 週報 第十二期
- LeetCode - #146 LRU 快取(Top 100)
- LeetCode - #145 二叉樹的後序遍歷
- 現今 Swift 包中的二進位制目標
- LeetCode - #125 驗證迴文串
- 解決 iOS 15 上 APP 莫名其妙地退出登入
- 用 SwiftLint 保持 Swift 風格一致
- TCA - SwiftUI 的救星?(一)
- Swift 中的熱過載
- 在 Swift 中編寫指令碼:Git Hooks
- LeetCode - #124 二叉樹中的最大路徑和(Top 100)