Skip to content

Commit a7c6190

Browse files
committed
stacked area chart style
1 parent 4a670c0 commit a7c6190

File tree

8 files changed

+135
-46
lines changed

8 files changed

+135
-46
lines changed

Examples/ChartsExamples/ChartsView.swift

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ struct ChartsView: View {
1111
@State var data5: [CGFloat] = (0...100).map { _ in .random(in: 0.1...0.3) }
1212
@State var data6: [CGFloat] = (0...100).map { _ in .random(in: 0.3...0.4) }
1313

14-
@State var matrixData1: [[CGFloat]] = (0..<50).map { _ in (0..<3).map { _ in CGFloat.random(in: 0.00...0.33) } }
14+
@State var matrixData1: [[CGFloat]] = (0..<20).map { _ in (0..<3).map { _ in CGFloat.random(in: 0.00...0.33) } }
1515

1616
var body: some View {
1717
ScrollView {
@@ -60,8 +60,30 @@ struct ChartsView: View {
6060
.chartStyle(
6161
StackedColumnChartStyle(spacing: 2, colors: [.yellow, .orange, .red])
6262
)
63+
.background(
64+
Color.gray.opacity(0.1)
65+
.overlay(
66+
GridPattern(horizontalLines: data2.count)
67+
.inset(by: 1)
68+
.stroke(Color.red.opacity(0.2), style: .init(lineWidth: 1, lineCap: .round))
69+
)
70+
)
71+
.cornerRadius(16)
72+
.frame(height: 300)
6373
.padding()
64-
.background(Color.gray.opacity(0.1))
74+
75+
Chart(data: matrixData1)
76+
.chartStyle(
77+
StackedAreaChartStyle(.quadCurve, colors: [.yellow, .orange, .red])
78+
)
79+
.background(
80+
Color.gray.opacity(0.1)
81+
.overlay(
82+
GridPattern(horizontalLines: data2.count)
83+
.inset(by: 1)
84+
.stroke(Color.red.opacity(0.2), style: .init(lineWidth: 1, lineCap: .round))
85+
)
86+
)
6587
.cornerRadius(16)
6688
.frame(height: 300)
6789
.padding()
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import SwiftUI
2+
import Shapes
3+
4+
struct AreaChart: Shape {
5+
private let lineType: LineType
6+
private let unitPoints: [UnitPoint]
7+
private let bottomUnitPoints: [UnitPoint]?
8+
9+
public func path(in rect: CGRect) -> Path {
10+
Path { path in
11+
switch self.lineType {
12+
case .line:
13+
path.addLines(self.unitPoints.points(in: rect))
14+
case .quadCurve:
15+
path.addQuadCurves(self.unitPoints.points(in: rect))
16+
}
17+
18+
if let bottomUnitPoints = bottomUnitPoints {
19+
switch self.lineType {
20+
case .line:
21+
bottomUnitPoints.reversed().forEach {
22+
path.addLine(to: CGPoint(unitPoint: $0, in: rect))
23+
}
24+
case .quadCurve:
25+
path.addQuadCurves(bottomUnitPoints.reversed().points(in: rect))
26+
}
27+
} else {
28+
path.addLine(to: CGPoint(unitPoint: .topTrailing, in: rect))
29+
path.addLine(to: CGPoint(unitPoint: .topLeading, in: rect))
30+
}
31+
path.closeSubpath()
32+
}
33+
}
34+
35+
init<Data: RandomAccessCollection>(unitData: Data, bottomUnitData: Data? = nil, lineType: LineType) where Data.Element : BinaryFloatingPoint {
36+
self.lineType = lineType
37+
let step: CGFloat = unitData.count > 1 ? 1.0 / CGFloat(unitData.count - 1) : 1.0
38+
self.unitPoints = unitData.enumerated().map { (index, dataPoint) in UnitPoint(x: step * CGFloat(index), y: CGFloat(dataPoint)) }
39+
self.bottomUnitPoints = bottomUnitData?.enumerated().map { (index, dataPoint) in UnitPoint(x: step * CGFloat(index), y: CGFloat(dataPoint)) }
40+
}
41+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import SwiftUI
2+
import Shapes
3+
4+
public struct AreaChartStyle<Fill: View>: ChartStyle {
5+
private let lineType: LineType
6+
private let fill: Fill
7+
8+
public func makeBody(configuration: Self.Configuration) -> some View {
9+
fill
10+
.clipShape(
11+
AreaChart(unitData: configuration.dataMatrix.map { $0.reduce(0, +) }, lineType: self.lineType)
12+
)
13+
}
14+
15+
public init(_ lineType: LineType = .quadCurve, fill: Fill) {
16+
self.lineType = lineType
17+
self.fill = fill
18+
}
19+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import SwiftUI
2+
import Shapes
3+
4+
public struct StackedAreaChartStyle: ChartStyle {
5+
private let lineType: LineType
6+
private let colors: [Color]
7+
8+
public func makeBody(configuration: Self.Configuration) -> some View {
9+
ZStack {
10+
ForEach(Array(configuration.dataMatrix.flipDirection().stacked().reversed().enumerated()), id: \.self.offset) { enumeratedData in
11+
self.colors.prefix(configuration.dataMatrix.count).reversed()[enumeratedData.offset % self.colors.count].clipShape(
12+
AreaChart(
13+
unitData: enumeratedData.element,
14+
lineType: self.lineType
15+
)
16+
)
17+
}
18+
}
19+
.drawingGroup()
20+
}
21+
22+
public init(_ lineType: LineType = .quadCurve, colors: [Color] = [.red, .orange, .yellow, .green, .blue, .purple]) {
23+
self.lineType = lineType
24+
self.colors = colors
25+
}
26+
}
27+
28+
extension Collection where Element == [CGFloat] {
29+
func stacked() -> [[CGFloat]] {
30+
self.reduce([]) { (result, element) -> [[CGFloat]] in
31+
if let lastElement = result.last {
32+
return result + [zip(lastElement, element).compactMap(+)]
33+
} else {
34+
return [element]
35+
}
36+
}
37+
}
38+
}
39+
40+
extension Array where Element == [CGFloat] {
41+
func flipDirection() -> [[CGFloat]] {
42+
let columnsCount = self.first?.count ?? 0
43+
let rowCount = self.count
44+
45+
return (0..<columnsCount).map { columnIndex in
46+
(0..<rowCount).map { rowIndex in
47+
return self[rowIndex][columnIndex]
48+
}.reversed()
49+
}
50+
}
51+
}

Sources/Charts/Chart/Styles/AreaChartStyle.swift

Lines changed: 0 additions & 44 deletions
This file was deleted.

Sources/Charts/Chart/Styles/ColumnChartStyle.swift renamed to Sources/Charts/Chart/Styles/Column/ColumnChartStyle.swift

File renamed without changes.

Sources/Charts/Chart/Styles/StackedColumnChartStyle.swift renamed to Sources/Charts/Chart/Styles/Column/StackedColumnChartStyle.swift

File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)