How to Modify Swift Charts

Swift Charts Framework for iOS16+


A cheet sheet of useful constructs


A basic stock chart:

// a line chart using CandleStick structure

Chart(list) { candleStick in

LineMark(x: .value("tick", candleStick.timestamp ),

y: .value("Price", candleStick.close))

.foregroundStyle(.green)

} // chart



Set the range of the X & Y Axis with:

.chartYScale(domain: priceMin...priceMax)

.chartXScale(domain: tickMin...tickMax)


Set the chart background color:

15 .chartBackground { chartProxy in

16 Color.red.opacity(0.1)

17 }

18 .frame(height:200)


Set the plotting area frame via:

.chartPlotStyle { plotArea in

plotArea

.frame(width: 400, height: 150)

.background(Color(.secondarySystemBackground))

}

——

15 .chartPlotStyle { plotArea in

16 plotArea

17 .background(.orange.opacity(0.1))

18 .border(.orange, width: 2)

19 }

20 .frame(height:200)


The basics of an Axis Label


// .chartXAxis {

// AxisMarks(values: .stride(by: .day)) { value in

// AxisGridLine()

// AxisTick()

// AxisValueLabel()

// }

// }



Hide the Y axis & Legend

//.chartXAxis(.hidden)

.chartYAxis(.hidden)

.chartLegend(.hidden)

.padding()


Move Y Axis to left edge:

22 // Place the y-axis on the leading side of the chart

23 .chartYAxis {

24 AxisMarks(position: .leading)

25 }


Move the chart legend:

5 // Position the Legend

16 .chartLegend(position: .overlay, alignment: .top)

17

————

15 // Position the Legend

16 .chartLegend(position: .trailing, alignment: .center, spacing: 10)

17



Round the Line with interpolationMethod .catmullRom :

5 Chart {

6 ForEach(stepData, id: \.period) { steps in

7 ForEach(steps.data) {

8 LineMark(

9 x: .value("Week Day", $0.shortDay),

10 y: .value("Step Count", $0.steps)

11 )

12 .foregroundStyle(by: .value("Week", steps.period))

13 // Use curved line to join points

14 .interpolationMethod(.catmullRom)

15 .accessibilityLabel("\($0.weekdayString)")

16 .accessibilityValue("\($0.steps) Steps")

17 }

18 }

19 }


Changing line colors with chartForegroundStyleScale:

5 Chart {

6 ForEach(stepData, id: \.period) { steps in

7 ForEach(steps.data) {

8 LineMark(

9 x: .value("Week Day", $0.shortDay),

10 y: .value("Step Count", $0.steps)

11 )

12 .foregroundStyle(by: .value("Week", steps.period))

13 .interpolationMethod(.catmullRom)

14 .symbol(by: .value("Week", steps.period))

15 .symbolSize(30)

16 .accessibilityLabel("\($0.weekdayString)")

17 .accessibilityValue("\($0.steps) Steps")

18 }

19 }

20 }

21 // Set color for each data in the chart

22 .chartForegroundStyleScale([

23 "Current Week" : Color(hue: 0.33, saturation: 0.81, brightness: 0.76),

24 "Previous Week": Color(hue: 0.69, saturation: 0.19, brightness: 0.79)

25 ])

26 .chartLegend(position: .overlay, alignment: .top)




Changing the Line Style with .lineStyle & StrokeStyle() :

9 Chart {

10 ForEach(previousWeek) {

11 LineMark(

12 x: .value("Week Day", $0.shortDay),

13 y: .value("Step Count", $0.steps)

14 )

15 .interpolationMethod(.catmullRom)

16 .foregroundStyle(prevColor)

17 .foregroundStyle(by: .value("Week", "Previous Week"))

18 .lineStyle(StrokeStyle(lineWidth: 3, dash: [5, 10]))

19 .symbol() {

20 Rectangle()

21 .fill(prevColor)

22 .frame(width: 8, height: 8)

23 }

24 .symbolSize(30)

25 .accessibilityLabel("\($0.weekdayString)")

26 .accessibilityValue("\($0.steps) Steps")

27 }


Examples from: https://swdevnotes.com/swift/2022/customise-a-line-chart-with-swiftui-charts-in-ios-16/


https://github.com/calleric/swift/blob/main/LineAreaChartView.swift



1struct ChartView1: View {

2 var body: some View {

3 VStack {

4 GroupBox ( "Line Chart - Daily Step Count") {

5 Chart {

6 ForEach(stepData, id: \.period) { steps in

7 ForEach(steps.data) {

8 LineMark(

9 x: .value("Week Day", $0.shortDay),

10 y: .value("Step Count", $0.steps)

11 )

12 .foregroundStyle(by: .value("Week", steps.period))

13 .accessibilityLabel("\($0.weekdayString)")

14 .accessibilityValue("\($0.steps) Steps")

15 }

16 }

17 }

18 .frame(height:400)

19 }

20 .padding()


// Add a style to the GroupBox

21 .groupBoxStyle(YellowGroupBoxStyle())

22 .padding()

23


1struct YellowGroupBoxStyle: GroupBoxStyle {

2 func makeBody(configuration: Configuration) -> some View {

3 configuration.content

4 .padding(.top, 30)

5 .padding(20)

6 .background(Color(hue: 0.10, saturation: 0.10, brightness: 0.98))

7 .cornerRadius(20)

8 .overlay(

9 configuration.label.padding(10),

10 alignment: .topLeading

11 )

12 }

13}




———


Combine multiple style charts on same plot :


1struct ChartView9: View {

2

3 var body: some View {

4

5 let prevColor = Color(hue: 0.69, saturation: 0.19, brightness: 0.79)

6 let curColor = Color(hue: 0.33, saturation: 0.81, brightness: 0.76)

7 let curGradient = LinearGradient(

8 gradient: Gradient (

9 colors: [

10 curColor.opacity(0.5),

11 curColor.opacity(0.2),

12 curColor.opacity(0.05),

13 ]

14 ),

15 startPoint: .top,

16 endPoint: .bottom

17 )

18

19 VStack() {

20 GroupBox ( "Line Chart - Combine LIne and Area chart") {

21 Chart {

22 ForEach(previousWeek) {

23 LineMark(

24 x: .value("Week Day", $0.shortDay),

25 y: .value("Step Count", $0.steps)

26 )

27 .interpolationMethod(.catmullRom)

28 .foregroundStyle(prevColor)

29 .foregroundStyle(by: .value("Week", "Previous Week"))

30 .lineStyle(StrokeStyle(lineWidth: 3, dash: [5, 10]))

31 .symbol() {

32 Rectangle()

33 .fill(prevColor)

34 .frame(width: 8, height: 8)

35 }

36 .symbolSize(30)

37 .accessibilityLabel("\($0.weekdayString)")

38 .accessibilityValue("\($0.steps) Steps")

39 }

40

41 ForEach(currentWeek) {

42 LineMark(

43 x: .value("Week Day", $0.shortDay),

44 y: .value("Step Count", $0.steps)

45 )

46 .interpolationMethod(.catmullRom)

47 .foregroundStyle(curColor)

48 .foregroundStyle(by: .value("Week", "Current Week"))

49 .lineStyle(StrokeStyle(lineWidth: 3))

50 .symbol() {

51 Circle()

52 .fill(curColor)

53 .frame(width: 10)

54 }

55 .symbolSize(30)

56 .accessibilityLabel("\($0.weekdayString)")

57 .accessibilityValue("\($0.steps) Steps")

58

59 AreaMark(

60 x: .value("Week Day", $0.shortDay),

61 y: .value("Step Count", $0.steps)

62 )

63 .interpolationMethod(.catmullRom)

64 .foregroundStyle(curGradient)

65 .foregroundStyle(by: .value("Week", "Current Week"))

66 .accessibilityLabel("\($0.weekdayString)")

67 .accessibilityValue("\($0.steps) Steps")

68

69 }

70 }

71 // Set the Y axis scale

72 .chartYScale(domain: 0...30000)

73

74 .chartForegroundStyleScale([

75 "Current Week" : curColor,

76 "Previous Week": prevColor

77 ])

78 .chartLegend(position: .overlay, alignment: .top)

79 .chartPlotStyle { plotArea in

80 plotArea

81 .background(Color(hue: 0.12, saturation: 0.10, brightness: 0.92))

82 }

83 .chartYAxis() {

84 AxisMarks(position: .leading)

85 }

86 .frame(height:400)

87 }

88 .groupBoxStyle(YellowGroupBoxStyle())

89 Spacer()

90 }

91 .padding()

92 }

93}




——


From: How FAST is Swift Charts? Can it handle a sound visualizer? - SwiftUI - iOS 16 https://www.youtube.com/watch?v=8kX1CX-ujlA

https://github.com/vNakamura/SwiftChartsAudioVisualizer


Using an array index as the X value with Chart(Array(data.enumerated()), id: \.0) { index, magnitude in


@State var data: [Float] = Array(repeating: 0, count: Constants.barAmount).map { _ in Float.random(in: 1 ... Constants.magnitudeLimit) }


var body: some View {

VStack {

Spacer()

VStack {

Chart(Array(data.enumerated()), id: \.0) { index, magnitude in

BarMark(

x: .value("Frequency", String(index)),

y: .value("Magnitude", magnitude)

)

.foregroundStyle(


Color(hue: 0.3 - Double((magnitude / Constants.magnitudeLimit) / 5),

saturation: 1,

brightness: 1,

opacity: 0.7

)


)

}

.onReceive(timer, perform: updateData)


.... [more code] ....