1、前言
上一篇介紹了Charts在Watch中的基本使用,這一篇我們介紹個(gè)性化設(shè)置,使得圖表更符合我們的實(shí)際需求和風(fēng)格樣式。
2、修改Y軸范圍
還是使用上一篇的銷售量示例,默認(rèn)情況下Y軸范圍會(huì)根據(jù)實(shí)際數(shù)據(jù)自動(dòng)判斷,如示例中Y軸范圍為0-3000,假設(shè)需求是為了凸顯銷量目標(biāo)為4000,期望Y軸的范圍為0-4000,只需要添加一行代碼
.chartYScale(domain: 0...4000)
完整代碼為:
import SwiftUI
import Charts
struct ContentView: View {
var body: some View {
let gzData:[(day:Date,sales:Int)] = [
(day:Util.getDate(offset:1),sales:1666),
(day:Util.getDate(offset:2),sales:1899),
(day:Util.getDate(offset:3),sales:1254),
(day:Util.getDate(offset:4),sales:1200),
(day:Util.getDate(offset:5),sales:983),
(day:Util.getDate(offset:6),sales:1101),
(day:Util.getDate(offset:7),sales:801),
]
let szData:[(day:Date,sales:Int)] = [
(day:Util.getDate(offset:1),sales:2287),
(day:Util.getDate(offset:2),sales:1655),
(day:Util.getDate(offset:3),sales:1598),
(day:Util.getDate(offset:4),sales:1067),
(day:Util.getDate(offset:5),sales:900),
(day:Util.getDate(offset:6),sales:1201),
(day:Util.getDate(offset:7),sales:540),
]
let sericeData = [
(city:"廣州",data: gzData),
(city:"深圳",data: szData)
]
Chart {
ForEach(sericeData,id: \.city) { serice in
ForEach(serice.data,id: \.day) {
LineMark(
x: .value("日期", $0.day,unit: .day),
y: .value("銷售量", $0.sales)
)
}
.symbol(.circle)
.foregroundStyle(by: .value("City", serice.city))
}
}
.chartYScale(domain: 0...4000)
}
}
class Util {
static func getDate(offset:Int) -> Date {
let calendar = Calendar.current
return calendar.date(byAdding: .day, value: -offset, to: Date()) ?? Date()
}
}
運(yùn)行效果:

3、自定義數(shù)據(jù)顏色
上面的示例中,系統(tǒng)自動(dòng)選擇了藍(lán)色和綠色來(lái)區(qū)分廣州和深圳的數(shù)據(jù),如果需求是用紅色和綠色來(lái)表示廣州和深圳,也只需要添加簡(jiǎn)單的代碼:
Chart {
...
}
.chartYScale(domain: 0...4000)
.chartForegroundStyleScale([
"廣州": .red,
"深圳": .green
])

3、深度自定義
圖表區(qū)域可拆分為:
- Axes:xy軸
- Legend:圖例
- Plot Area:繪圖區(qū)域


這三個(gè)部分都可以根據(jù)我們的需求進(jìn)行自定義,我們先看下如果自定義軸和圖例。先上原始代碼:
import SwiftUI
import Charts
struct ContentView: View {
var body: some View {
let data = [
(month:Util.getDateForMonth(month:1),dailyAverage:939),
(month:Util.getDateForMonth(month:2),dailyAverage:879),
(month:Util.getDateForMonth(month:3),dailyAverage:840),
(month:Util.getDateForMonth(month:4),dailyAverage:823),
(month:Util.getDateForMonth(month:5),dailyAverage:797),
(month:Util.getDateForMonth(month:6),dailyAverage:800),
(month:Util.getDateForMonth(month:7),dailyAverage:820),
(month:Util.getDateForMonth(month:8),dailyAverage:834),
(month:Util.getDateForMonth(month:9),dailyAverage:791),
(month:Util.getDateForMonth(month:10),dailyAverage:801),
(month:Util.getDateForMonth(month:11),dailyAverage:765),
(month:Util.getDateForMonth(month:12),dailyAverage:489),
]
Chart(data,id: \.month) {
BarMark(
x: .value("月", $0.month, unit: .month),
y: .value("日均銷量", $0.dailyAverage)
)
}
}
}
class Util {
static func getDateForMonth(month: Int, year: Int = Calendar.current.component(.year, from: Date())) -> Date {
// 創(chuàng)建一個(gè) DateComponents 對(duì)象
var components = DateComponents()
components.year = year
components.month = month
components.day = 1 // 設(shè)置為該月的第一天
// 使用 Calendar 將 DateComponents 轉(zhuǎn)換為 Date
let calendar = Calendar.current
return calendar.date(from: components) ?? Date()
}
}
原始代碼運(yùn)行效果:

Charts會(huì)自動(dòng)生成X抽數(shù)據(jù),我們希望能完整顯示每個(gè)月,并且使用首字母作為月份標(biāo)簽,這時(shí)我們就可以使用.chartXAxis來(lái)實(shí)現(xiàn)自定義了,給Chart添加修飾器:
Chart(data,id: \.month) {
//...
}
.chartXAxis{
AxisMarks(values: .stride(by: .month))
}

AxisMarks()如果不帶參數(shù),x軸創(chuàng)建的就是默認(rèn)樣式
此時(shí)雖然顯示了所有月份,但是空間不夠全部被壓縮了,此時(shí)我們來(lái)完全自定義X軸樣式:
.chartXAxis{
AxisMarks(values: .stride(by: .month)) { value in
AxisGridLine()
AxisTick()
AxisValueLabel(
format: .dateTime.month(.narrow)
)
}
}

嘗試分別刪除AxisGridLine()和AxisTick(),看它們分別代表什么?
其中參數(shù)值value為當(dāng)前的軸的值,本例中為數(shù)據(jù)中month,類型為Date,此時(shí)我們可以根據(jù)Date的值,x軸只顯示每個(gè)季度的首月:
.chartXAxis{
AxisMarks(values: .stride(by: .month)) { value in
if value.as(Date.self)!.isFirstMonthOfQuarter() {
AxisGridLine().foregroundStyle(.white.opacity(0.3))
AxisTick().foregroundStyle(.white.opacity(0.3))
AxisValueLabel(
format: .dateTime.year().quarter()
)
}else {
AxisGridLine().foregroundStyle(.white.opacity(0.1))
}
}
}
其中isFirstMonthOfQuarter方法如下:
extension Date {
func isFirstMonthOfQuarter() -> Bool {
let calendar = Calendar.current
let month = calendar.component(.month, from: self)
return [1,4,7,10].contains(month)
}
}

我們?cè)賮?lái)看下Y軸,可以將Y軸移動(dòng)到左邊:
.chartYAxis() {
AxisMarks(position: .leading)
}

可以通過(guò)以下代碼分別隱藏X軸、Y軸、圖例:
.chartXAxis(.hidden)
.chartYAxis(.hidden)
.chartLegend(.hidden)
通過(guò) .chartPlotStyle可以拿到繪制區(qū)域上下文,進(jìn)行定制化:
.chartPlotStyle { ploatAr in
return ploatAr
}
例如固定設(shè)置繪制區(qū)的高度為100,添加背景色和邊框:
.chartPlotStyle { ploatAr in
ploatAr
.frame(height: 100)
.background(.pink.opacity(0.2))
.border(.pink,width: 1)
}

這篇先介紹到這里,下一篇將舉個(gè)??,將Charts應(yīng)用到實(shí)際開(kāi)發(fā)中。