Skip to content

Commit aa91264

Browse files
authored
Add PieChart + multicolor (#98)
* Add ColorGradient example Add ColorGradient single color constructor Add preview for BarChart * Add PieChart Allow multi color for Pie and Bar Add linter
1 parent a2d75dc commit aa91264

File tree

14 files changed

+472
-27
lines changed

14 files changed

+472
-27
lines changed

.swiftlint.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
disabled_rules:
2+
- explicit_acl
3+
- trailing_whitespace
4+
- force_cast
5+
- unused_closure_parameter
6+
- multiple_closures_with_trailing_closure
7+
opt_in_rules:
8+
- anyobject_protocol
9+
- array_init
10+
- attributes
11+
- collection_alignment
12+
- colon
13+
- conditional_returns_on_newline
14+
- convenience_type
15+
- empty_count
16+
- empty_string
17+
- empty_collection_literal
18+
- enum_case_associated_values_count
19+
- function_default_parameter_at_end
20+
- fatal_error_message
21+
- file_name
22+
- first_where
23+
- modifier_order
24+
- toggle_bool
25+
- unused_private_declaration
26+
- yoda_condition
27+
excluded:
28+
- Carthage
29+
- Pods
30+
- SwiftLint/Common/3rdPartyLib
31+
identifier_name:
32+
excluded:
33+
- a
34+
- b
35+
- c
36+
- i
37+
- id
38+
- t
39+
- to
40+
- x
41+
- y
42+
line_length:
43+
warning: 150
44+
error: 200
45+
ignores_function_declarations: true
46+
ignores_comments: true
47+
ignores_urls: true
48+
function_body_length:
49+
warning: 300
50+
error: 500
51+
function_parameter_count:
52+
warning: 6
53+
error: 8
54+
type_body_length:
55+
warning: 300
56+
error: 400
57+
file_length:
58+
warning: 500
59+
error: 1200
60+
ignore_comment_only_lines: true
61+
cyclomatic_complexity:
62+
warning: 15
63+
error: 21
64+
reporter: "xcode"

Package.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import PackageDescription
66
let package = Package(
77
name: "SwiftUICharts",
88
platforms: [
9-
.iOS(.v13),.watchOS(.v6)
9+
.iOS(.v13), .watchOS(.v6)
1010
],
1111
products: [
1212
// Products define the executables and libraries produced by a package, and make them visible to other packages.
1313
.library(
1414
name: "SwiftUICharts",
15-
targets: ["SwiftUICharts"]),
15+
targets: ["SwiftUICharts"])
1616
],
1717
dependencies: [
1818
// Dependencies declare other packages that this package depends on.
@@ -26,6 +26,6 @@ let package = Package(
2626
dependencies: []),
2727
.testTarget(
2828
name: "SwiftUIChartsTests",
29-
dependencies: ["SwiftUICharts"]),
29+
dependencies: ["SwiftUICharts"])
3030
]
3131
)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//
2+
// CGRect+Extension.swift
3+
// SwiftUICharts
4+
//
5+
// Created by Nicolas Savoini on 2020-05-24.
6+
//
7+
8+
import Foundation
9+
import SwiftUI
10+
11+
extension CGRect {
12+
// Return the coordinate for a rectangle center
13+
public var mid: CGPoint {
14+
return CGPoint(x: self.midX, y: self.midY)
15+
}
16+
}

Sources/SwiftUICharts/Base/Extensions/Color+Extension.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@ extension Color {
55
let hex = hexString.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
66
var int = UInt64()
77
Scanner(string: hex).scanHexInt64(&int)
8-
let r, g, b: UInt64
8+
let red, green, blue: UInt64
99
switch hex.count {
1010
case 3: // RGB (12-bit)
11-
(r, g, b) = ((int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
11+
(red, green, blue) = ((int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
1212
case 6: // RGB (24-bit)
13-
(r, g, b) = (int >> 16, int >> 8 & 0xFF, int & 0xFF)
13+
(red, green, blue) = (int >> 16, int >> 8 & 0xFF, int & 0xFF)
1414
case 8: // ARGB (32-bit)
15-
(r, g, b) = (int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
15+
(red, green, blue) = (int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
1616
default:
17-
(r, g, b) = (0, 0, 0)
17+
(red, green, blue) = (0, 0, 0)
1818
}
19-
self.init(red: Double(r) / 255, green: Double(g) / 255, blue: Double(b) / 255)
19+
self.init(red: Double(red) / 255, green: Double(green) / 255, blue: Double(blue) / 255)
2020
}
2121
}
Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,28 @@
11
import SwiftUI
22

33
public struct ChartStyle {
4-
public let backgroundColor: Color
5-
public let foregroundColor: ColorGradient
64

5+
public let backgroundColor: ColorGradient
6+
public let foregroundColor: [ColorGradient]
7+
8+
public init(backgroundColor: Color, foregroundColor: [ColorGradient]) {
9+
self.backgroundColor = ColorGradient.init(backgroundColor)
10+
self.foregroundColor = foregroundColor
11+
}
12+
713
public init(backgroundColor: Color, foregroundColor: ColorGradient) {
14+
self.backgroundColor = ColorGradient.init(backgroundColor)
15+
self.foregroundColor = [foregroundColor]
16+
}
17+
18+
public init(backgroundColor: ColorGradient, foregroundColor: ColorGradient) {
19+
self.backgroundColor = backgroundColor
20+
self.foregroundColor = [foregroundColor]
21+
}
22+
23+
public init(backgroundColor: ColorGradient, foregroundColor: [ColorGradient]) {
824
self.backgroundColor = backgroundColor
925
self.foregroundColor = foregroundColor
1026
}
27+
1128
}

Sources/SwiftUICharts/Base/Style/ColorGradient.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ public struct ColorGradient {
44
public let startColor: Color
55
public let endColor: Color
66

7+
public init(_ color: Color) {
8+
self.startColor = color
9+
self.endColor = color
10+
}
11+
712
public init (_ startColor: Color, _ endColor: Color) {
813
self.startColor = startColor
914
self.endColor = endColor
@@ -13,3 +18,21 @@ public struct ColorGradient {
1318
return Gradient(colors: [startColor, endColor])
1419
}
1520
}
21+
22+
extension ColorGradient {
23+
/// Convenience method to return a LinearGradient from the ColorGradient
24+
/// - Parameters:
25+
/// - startPoint: starting point
26+
/// - endPoint: ending point
27+
/// - Returns: a Linear gradient
28+
public func linearGradient(from startPoint: UnitPoint, to endPoint: UnitPoint) -> LinearGradient {
29+
return LinearGradient(gradient: self.gradient, startPoint: startPoint, endPoint: endPoint)
30+
}
31+
}
32+
33+
extension ColorGradient {
34+
public static let orangeBright = ColorGradient(ChartColors.orangeBright)
35+
public static let redBlack = ColorGradient(.red, .black)
36+
public static let greenRed = ColorGradient(.green, .red)
37+
public static let whiteBlack = ColorGradient(.white, .black)
38+
}

Sources/SwiftUICharts/Charts/BarChart/BarChart.swift

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,26 @@ import SwiftUI
22

33
public struct BarChart: ChartType {
44
public func makeChart(configuration: Self.Configuration, style: Self.Style) -> some View {
5-
BarChartRow(data: configuration.data, gradientColor: style.foregroundColor)
5+
BarChartRow(data: configuration.data, style: style)
66
}
7-
87
public init() {}
98
}
9+
10+
struct BarChart_Previews: PreviewProvider {
11+
static var previews: some View {
12+
Group {
13+
Group {
14+
BarChart().makeChart(
15+
configuration: .init(data: [1, 2, 3, 5, 1]),
16+
style: .init(backgroundColor: .white, foregroundColor: ColorGradient.redBlack))
17+
}.environment(\.colorScheme, .light)
18+
19+
Group {
20+
BarChart().makeChart(
21+
configuration: .init(data: [1, 2, 3]),
22+
style: .init(backgroundColor: .white, foregroundColor: ColorGradient.redBlack))
23+
}.environment(\.colorScheme, .dark)
24+
25+
}
26+
}
27+
}

Sources/SwiftUICharts/Charts/BarChart/BarChartCell.swift

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import SwiftUI
22

3-
public struct BarChartCell : View {
3+
public struct BarChartCell: View {
44
@State var value: Double
55
@State var index: Int = 0
66
@State var width: Float
@@ -13,16 +13,36 @@ public struct BarChartCell : View {
1313

1414
@State var scaleValue: Double = 0
1515
@Binding var touchLocation: CGFloat
16+
1617
public var body: some View {
1718
ZStack {
1819
RoundedRectangle(cornerRadius: 4)
19-
.fill(LinearGradient(gradient: gradientColor.gradient, startPoint: .bottom, endPoint: .top))
20+
.fill(gradientColor.linearGradient(from: .bottom, to: .top))
2021
}
2122
.frame(width: CGFloat(self.cellWidth))
2223
.scaleEffect(CGSize(width: 1, height: self.scaleValue), anchor: .bottom)
23-
.onAppear(){
24+
.onAppear {
2425
self.scaleValue = self.value
2526
}
2627
.animation(Animation.spring().delay(self.touchLocation < 0 ? Double(self.index) * 0.04 : 0))
2728
}
2829
}
30+
31+
struct BarChartCell_Previews: PreviewProvider {
32+
static var previews: some View {
33+
Group {
34+
Group {
35+
BarChartCell(value: 1, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient.greenRed, touchLocation: .constant(CGFloat()))
36+
BarChartCell(value: 1, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient.whiteBlack, touchLocation: .constant(CGFloat()))
37+
BarChartCell(value: 1, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient(.purple), touchLocation: .constant(CGFloat()))
38+
}
39+
40+
Group {
41+
BarChartCell(value: 1, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient.greenRed, touchLocation: .constant(CGFloat()))
42+
BarChartCell(value: 1, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient.whiteBlack, touchLocation: .constant(CGFloat()))
43+
BarChartCell(value: 1, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient(.purple), touchLocation: .constant(CGFloat()))
44+
}.environment(\.colorScheme, .dark)
45+
}
46+
47+
}
48+
}

Sources/SwiftUICharts/Charts/BarChart/BarChartRow.swift

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import SwiftUI
22

3-
public struct BarChartRow : View {
3+
public struct BarChartRow: View {
44
@State var data: [Double] = []
55
@State var touchLocation: CGFloat = -1.0
66

77
enum Constant {
88
static let spacing: CGFloat = 16.0
99
}
1010

11-
var gradientColor: ColorGradient
11+
var style: ChartStyle
12+
1213
var maxValue: Double {
1314
data.max() ?? 0
1415
}
@@ -17,14 +18,14 @@ public struct BarChartRow : View {
1718
GeometryReader { geometry in
1819
HStack(alignment: .bottom,
1920
spacing: (geometry.frame(in: .local).width - Constant.spacing) / CGFloat(self.data.count * 3)) {
20-
ForEach(0..<self.data.count, id: \.self) { i in
21-
BarChartCell(value: self.normalizedValue(index: i),
22-
index: i,
21+
ForEach(0..<self.data.count, id: \.self) { index in
22+
BarChartCell(value: self.normalizedValue(index: index),
23+
index: index,
2324
width: Float(geometry.frame(in: .local).width - Constant.spacing),
2425
numberOfDataPoints: self.data.count,
25-
gradientColor: self.gradientColor,
26+
gradientColor: self.style.foregroundColor.rotate(for: index),
2627
touchLocation: self.$touchLocation)
27-
.scaleEffect(self.getScaleSize(touchLocation: self.touchLocation, index: i), anchor: .bottom)
28+
.scaleEffect(self.getScaleSize(touchLocation: self.touchLocation, index: index), anchor: .bottom)
2829
.animation(.spring())
2930

3031
}
@@ -52,4 +53,26 @@ public struct BarChartRow : View {
5253
}
5354
return CGSize(width: 1, height: 1)
5455
}
56+
5557
}
58+
59+
struct BarChartRow_Previews: PreviewProvider {
60+
static var previews: some View {
61+
Group {
62+
Group {
63+
BarChartRow(data: [1, 2, 3], style: styleGreenRed)
64+
BarChartRow(data: [1, 2, 3], style: styleGreenRedWhiteBlack)
65+
}
66+
Group {
67+
BarChartRow(data: [1, 2, 3], style: styleGreenRed)
68+
BarChartRow(data: [1, 2, 3], style: styleGreenRedWhiteBlack)
69+
}.environment(\.colorScheme, .dark)
70+
}
71+
}
72+
}
73+
74+
private let styleGreenRed = ChartStyle(backgroundColor: .white, foregroundColor: .greenRed)
75+
76+
private let styleGreenRedWhiteBlack = ChartStyle(
77+
backgroundColor: ColorGradient.init(.white),
78+
foregroundColor: [ColorGradient.redBlack, ColorGradient.whiteBlack])

Sources/SwiftUICharts/Charts/LineChart/Line.swift

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import SwiftUI
33
public struct Line: View {
44
@State var frame: CGRect = .zero
55
@State var data: [Double]
6-
var gradientColor: ColorGradient
6+
var style: ChartStyle
77

88
@State var showIndicator: Bool = false
99
@State var touchLocation: CGPoint = .zero
@@ -79,7 +79,7 @@ extension Line {
7979

8080
private func getBackgroundPathView() -> some View {
8181
self.closedPath
82-
.fill(LinearGradient(gradient: gradientColor.gradient, startPoint: .bottom, endPoint: .top))
82+
.fill(style.backgroundColor.linearGradient(from: .bottom, to: .top))
8383
.rotationEffect(.degrees(180), anchor: .center)
8484
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
8585
.transition(.opacity)
@@ -89,7 +89,7 @@ extension Line {
8989
private func getLinePathView() -> some View {
9090
self.path
9191
.trim(from: 0, to: self.showFull ? 1:0)
92-
.stroke(LinearGradient(gradient: gradientColor.gradient,
92+
.stroke(LinearGradient(gradient: style.foregroundColor.first?.gradient ?? ColorGradient.orangeBright.gradient,
9393
startPoint: .leading,
9494
endPoint: .trailing),
9595
style: StrokeStyle(lineWidth: 3, lineJoin: .round))
@@ -105,3 +105,15 @@ extension Line {
105105
.drawingGroup()
106106
}
107107
}
108+
109+
struct Line_Previews: PreviewProvider {
110+
static var previews: some View {
111+
Group {
112+
Line(data: [1, 2, 3, 1, 2, 5, 7], style: blackLineStyle)
113+
Line(data: [1, 2, 3, 1, 2, 5, 7], style: redLineStyle)
114+
}
115+
}
116+
}
117+
118+
private let blackLineStyle = ChartStyle(backgroundColor: ColorGradient(.white), foregroundColor: ColorGradient(.black))
119+
private let redLineStyle = ChartStyle(backgroundColor: .whiteBlack, foregroundColor: ColorGradient(.red))

0 commit comments

Comments
 (0)