Skip to content

Commit f9da4a7

Browse files
authored
Merge pull request #17 from SwiftUIExtensions/stacked-area-chart-style
Stacked area chart style
2 parents 4a670c0 + 1bc196b commit f9da4a7

File tree

9 files changed

+156
-49
lines changed

9 files changed

+156
-49
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()

README.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Open `/Examples/ChartsExamples.xcodeproj` for more examples for iOS, macOS
1010

1111
## Styles
1212

13-
### LineChartStyle
13+
### Line
1414
<center>
1515
<img src="Resources/lineChart.png"/>
1616
</center>
@@ -22,7 +22,7 @@ Chart(data: [0.1, 0.3, 0.2, 0.5, 0.4, 0.9, 0.1])
2222
)
2323
```
2424

25-
### AreaChartStyle
25+
### Area
2626
<center>
2727
<img src="Resources/areaChart.png"/>
2828
</center>
@@ -36,7 +36,16 @@ Chart(data: [0.1, 0.3, 0.2, 0.5, 0.4, 0.9, 0.1])
3636
)
3737
```
3838

39-
### ColumnChartStyle
39+
### Stacked Area
40+
41+
```swift
42+
Chart(data: matrix)
43+
.chartStyle(
44+
StackedAreaChartStyle(.quadCurve, colors: [.yellow, .orange, .red])
45+
)
46+
```
47+
48+
### Column
4049
<center>
4150
<img src="Resources/columnChart.png"/>
4251
</center>
@@ -48,6 +57,15 @@ Chart(data: [0.1, 0.3, 0.2, 0.5, 0.4, 0.9, 0.1])
4857
)
4958
```
5059

60+
### Stacked Column
61+
62+
```swift
63+
Chart(data: matrix)
64+
.chartStyle(
65+
StackedColumnChartStyle(spacing: 2, colors: [.yellow, .orange, .red])
66+
)
67+
```
68+
5169
## SDKs
5270
- iOS 13+
5371
- Mac Catalyst 13.0+
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)