Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7853476d86 |
@@ -7,7 +7,7 @@
|
||||
<key>SwiftUICharts.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>2</integer>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
|
||||
@@ -1,29 +1,23 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct CardView<Content: View>: View, ChartBase {
|
||||
public var chartData = ChartData()
|
||||
public struct CardView<Content: View>: View {
|
||||
@Environment(\.chartStyle) private var chartStyle
|
||||
|
||||
let content: () -> Content
|
||||
|
||||
private var showShadow: Bool
|
||||
|
||||
@EnvironmentObject var style: ChartStyle
|
||||
|
||||
public init(showShadow: Bool = true, @ViewBuilder content: @escaping () -> Content) {
|
||||
self.showShadow = showShadow
|
||||
public init(@ViewBuilder content: @escaping () -> Content) {
|
||||
self.content = content
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
ZStack{
|
||||
if showShadow {
|
||||
RoundedRectangle(cornerRadius: 20)
|
||||
.fill(Color.white)
|
||||
.shadow(color: Color.gray, radius: 8)
|
||||
}
|
||||
Rectangle()
|
||||
.fill(self.chartStyle.backgroundColor.linearGradient(from: .bottom, to: .top))
|
||||
.cornerRadius(20)
|
||||
.shadow(color: Color.gray, radius: 8)
|
||||
VStack {
|
||||
self.content()
|
||||
}
|
||||
.clipShape(RoundedRectangle(cornerRadius: 20))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import SwiftUI
|
||||
|
||||
struct AnyChartType: ChartType {
|
||||
private let chartMaker: (ChartType.Configuration, ChartType.Style) -> AnyView
|
||||
|
||||
init<S: ChartType>(_ type: S) {
|
||||
self.chartMaker = type.makeTypeErasedBody
|
||||
}
|
||||
|
||||
func makeChart(configuration: ChartType.Configuration, style: ChartType.Style) -> AnyView {
|
||||
self.chartMaker(configuration, style)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension ChartType {
|
||||
func makeTypeErasedBody(configuration: ChartType.Configuration, style: ChartType.Style) -> AnyView {
|
||||
AnyView(makeChart(configuration: configuration, style: style))
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import SwiftUI
|
||||
|
||||
public protocol ChartBase {
|
||||
var chartData: ChartData { get }
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import SwiftUI
|
||||
|
||||
public class ChartData: ObservableObject {
|
||||
@Published public var data: [Double] = []
|
||||
|
||||
public init(_ data: [Double]) {
|
||||
self.data = data
|
||||
}
|
||||
|
||||
public init() {
|
||||
self.data = []
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import SwiftUI
|
||||
|
||||
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
|
||||
public protocol ChartType {
|
||||
associatedtype Body: View
|
||||
|
||||
func makeChart(configuration: Self.Configuration, style: Self.Style) -> Self.Body
|
||||
|
||||
typealias Configuration = ChartTypeConfiguration
|
||||
typealias Style = ChartStyle
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct ChartTypeConfiguration {
|
||||
public let data: [Double]
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import SwiftUI
|
||||
|
||||
public class ChartValue: ObservableObject {
|
||||
@Published var currentValue: Double = 0
|
||||
@Published var interactionInProgress: Bool = false
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// ChartView.swift
|
||||
// Created by Samu András on 2020. 05. 22..
|
||||
|
||||
import SwiftUI
|
||||
|
||||
public struct ChartView: View {
|
||||
@Environment(\.chartType) private var chartType
|
||||
@Environment(\.chartStyle) private var chartStyle
|
||||
|
||||
private var configuration: ChartTypeConfiguration
|
||||
|
||||
public var body: some View {
|
||||
self.chartType.makeChart(configuration: configuration, style: chartStyle)
|
||||
}
|
||||
}
|
||||
|
||||
extension ChartView {
|
||||
public init(data: [Double]) {
|
||||
self.configuration = ChartTypeConfiguration(data: data)
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// File.swift
|
||||
//
|
||||
//
|
||||
// Created by Nicolas Savoini on 2020-05-25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Array where Element == ColorGradient {
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//
|
||||
// CGRect+Extension.swift
|
||||
// SwiftUICharts
|
||||
//
|
||||
// Created by Nicolas Savoini on 2020-05-24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import SwiftUI
|
||||
|
||||
extension View where Self: ChartBase {
|
||||
public func data(_ data: [Double]) -> some View {
|
||||
chartData.data = data
|
||||
return self
|
||||
.environmentObject(chartData)
|
||||
.environmentObject(ChartValue())
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
import SwiftUI
|
||||
|
||||
extension View {
|
||||
public func chartStyle(_ style: ChartStyle) -> some View {
|
||||
self.environmentObject(style)
|
||||
public func type<S>(_ type: S) -> some View where S: ChartType {
|
||||
self.environment(\.chartType, AnyChartType(type))
|
||||
}
|
||||
|
||||
public func style(_ style: ChartStyle) -> some View {
|
||||
self.environment(\.chartStyle, style)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct ChartGrid<Content: View>: View, ChartBase {
|
||||
public var chartData = ChartData()
|
||||
let content: () -> Content
|
||||
|
||||
@EnvironmentObject var data: ChartData
|
||||
@EnvironmentObject var style: ChartStyle
|
||||
|
||||
public init(@ViewBuilder content: @escaping () -> Content) {
|
||||
self.content = content
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
ZStack{
|
||||
self.content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +1,19 @@
|
||||
import SwiftUI
|
||||
|
||||
public enum ChartLabelSize: CGFloat {
|
||||
case small = 16.0
|
||||
case normal = 24.0
|
||||
case large = 32.0
|
||||
}
|
||||
|
||||
public enum ChartLabelType {
|
||||
case title
|
||||
case subTitle
|
||||
case largeTitle
|
||||
case custom(size: CGFloat, padding: EdgeInsets, color: Color)
|
||||
case legend
|
||||
}
|
||||
|
||||
public struct ChartLabel: View {
|
||||
@EnvironmentObject var chartValue: ChartValue
|
||||
@State var textToDisplay:String = ""
|
||||
|
||||
private var title: String
|
||||
|
||||
private var labelSize: CGFloat {
|
||||
switch labelType {
|
||||
case .title:
|
||||
return 32.0
|
||||
case .legend:
|
||||
return 14.0
|
||||
case .subTitle:
|
||||
return 24.0
|
||||
case .largeTitle:
|
||||
return 38.0
|
||||
case .custom(let size, _, _):
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
private var labelPadding: EdgeInsets {
|
||||
switch labelType {
|
||||
case .title:
|
||||
return EdgeInsets(top: 16.0, leading: 8.0, bottom: 0.0, trailing: 8.0)
|
||||
case .legend:
|
||||
return EdgeInsets(top: 4.0, leading: 8.0, bottom: 0.0, trailing: 8.0)
|
||||
case .subTitle:
|
||||
return EdgeInsets(top: 8.0, leading: 8.0, bottom: 0.0, trailing: 8.0)
|
||||
case .largeTitle:
|
||||
return EdgeInsets(top: 24.0, leading: 8.0, bottom: 0.0, trailing: 8.0)
|
||||
case .custom(_, let padding, _):
|
||||
return padding
|
||||
}
|
||||
}
|
||||
|
||||
private let text: String
|
||||
private let labelSize: ChartLabelSize
|
||||
private let labelType: ChartLabelType
|
||||
|
||||
private var labelColor: Color {
|
||||
@@ -52,37 +22,22 @@ public struct ChartLabel: View {
|
||||
return .black
|
||||
case .legend:
|
||||
return .gray
|
||||
case .subTitle:
|
||||
return .black
|
||||
case .largeTitle:
|
||||
return .black
|
||||
case .custom(_, _, let color):
|
||||
return color
|
||||
}
|
||||
}
|
||||
|
||||
public init (_ title: String,
|
||||
type: ChartLabelType = .title) {
|
||||
self.title = title
|
||||
public init (_ text: String,
|
||||
type: ChartLabelType = .title,
|
||||
size: ChartLabelSize = .normal) {
|
||||
self.text = text
|
||||
labelType = type
|
||||
labelSize = size
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
HStack {
|
||||
Text(textToDisplay)
|
||||
.font(.system(size: labelSize))
|
||||
.bold()
|
||||
.foregroundColor(self.labelColor)
|
||||
.padding(self.labelPadding)
|
||||
.onAppear {
|
||||
self.textToDisplay = self.title
|
||||
}
|
||||
.onReceive(self.chartValue.objectWillChange) { _ in
|
||||
self.textToDisplay = self.chartValue.interactionInProgress ? String(format: "%.01f", self.chartValue.currentValue) : self.title
|
||||
}
|
||||
if !self.chartValue.interactionInProgress {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
Text(self.text)
|
||||
.font(.system(size: labelSize.rawValue))
|
||||
.bold()
|
||||
.foregroundColor(self.labelColor)
|
||||
.padding([.top, .bottom], 16.0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import SwiftUI
|
||||
|
||||
public class ChartStyle: ObservableObject {
|
||||
public struct ChartStyle {
|
||||
|
||||
public let backgroundColor: ColorGradient
|
||||
public let foregroundColor: [ColorGradient]
|
||||
|
||||
@@ -2,9 +2,9 @@ import SwiftUI
|
||||
|
||||
public enum ChartColors {
|
||||
// Orange
|
||||
public static let orangeBright = Color(hexString: "#FF782C")
|
||||
public static let orangeDark = Color(hexString: "#EC2301")
|
||||
static let orangeBright = Color(hexString: "#FF782C")
|
||||
static let orangeDark = Color(hexString: "#EC2301")
|
||||
|
||||
public static let legendColor: Color = Color(hexString: "#E8E7EA")
|
||||
public static let indicatorKnob: Color = Color(hexString: "#FF57A6")
|
||||
static let legendColor: Color = Color(hexString: "#E8E7EA")
|
||||
static let indicatorKnob: Color = Color(hexString: "#FF57A6")
|
||||
}
|
||||
|
||||
@@ -1,14 +1,30 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct BarChart: View, ChartBase {
|
||||
public var chartData = ChartData()
|
||||
|
||||
@EnvironmentObject var data: ChartData
|
||||
@EnvironmentObject var style: ChartStyle
|
||||
|
||||
public var body: some View {
|
||||
BarChartRow(chartData: data, style: style)
|
||||
public struct BarChart: ChartType {
|
||||
public func makeChart(configuration: Self.Configuration, style: Self.Style) -> some View {
|
||||
BarChartRow(data: configuration.data, style: style)
|
||||
}
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
struct BarChart_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
BarChart().makeChart(
|
||||
configuration: .init(data: [0]),
|
||||
style: .init(backgroundColor: .white, foregroundColor: ColorGradient.redBlack))
|
||||
Group {
|
||||
BarChart().makeChart(
|
||||
configuration: .init(data: [1, 2, 3, 5, 1]),
|
||||
style: .init(backgroundColor: .white, foregroundColor: ColorGradient.redBlack))
|
||||
}.environment(\.colorScheme, .light)
|
||||
|
||||
Group {
|
||||
BarChart().makeChart(
|
||||
configuration: .init(data: [1, 2, 3]),
|
||||
style: .init(backgroundColor: .white, foregroundColor: ColorGradient.redBlack))
|
||||
}.environment(\.colorScheme, .dark)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,67 +1,49 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct BarChartCell: View {
|
||||
var value: Double
|
||||
var index: Int = 0
|
||||
var width: Float
|
||||
var numberOfDataPoints: Int
|
||||
@State var value: Double
|
||||
@State var index: Int = 0
|
||||
@State var width: Float
|
||||
@State var numberOfDataPoints: Int
|
||||
var gradientColor: ColorGradient
|
||||
var touchLocation: CGFloat
|
||||
|
||||
var cellWidth: Double {
|
||||
return Double(width)/(Double(numberOfDataPoints) * 1.5)
|
||||
}
|
||||
|
||||
@State var firstDisplay: Bool = true
|
||||
|
||||
public init( value: Double,
|
||||
index: Int = 0,
|
||||
width: Float,
|
||||
numberOfDataPoints: Int,
|
||||
gradientColor: ColorGradient,
|
||||
touchLocation: CGFloat) {
|
||||
self.value = value
|
||||
self.index = index
|
||||
self.width = width
|
||||
self.numberOfDataPoints = numberOfDataPoints
|
||||
self.gradientColor = gradientColor
|
||||
self.touchLocation = touchLocation
|
||||
}
|
||||
|
||||
@State var scaleValue: Double = 0
|
||||
@Binding var touchLocation: CGFloat
|
||||
|
||||
public var body: some View {
|
||||
ZStack {
|
||||
RoundedRectangle(cornerRadius: 4)
|
||||
.fill(gradientColor.linearGradient(from: .bottom, to: .top))
|
||||
}
|
||||
.frame(width: CGFloat(self.cellWidth))
|
||||
.scaleEffect(CGSize(width: 1, height: self.firstDisplay ? 0.0 : self.value), anchor: .bottom)
|
||||
.onAppear {
|
||||
self.firstDisplay = false
|
||||
}
|
||||
.onDisappear {
|
||||
self.firstDisplay = true
|
||||
}
|
||||
.transition(.slide)
|
||||
.animation(Animation.spring().delay(self.touchLocation < 0 || !firstDisplay ? Double(self.index) * 0.04 : 0))
|
||||
}
|
||||
.frame(width: CGFloat(self.cellWidth))
|
||||
.scaleEffect(CGSize(width: 1, height: self.scaleValue), anchor: .bottom)
|
||||
.onAppear {
|
||||
self.scaleValue = self.value
|
||||
}
|
||||
.animation(Animation.spring().delay(self.touchLocation < 0 ? Double(self.index) * 0.04 : 0))
|
||||
}
|
||||
}
|
||||
|
||||
struct BarChartCell_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
BarChartCell(value: 0, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient.greenRed, touchLocation: .constant(CGFloat()))
|
||||
Group {
|
||||
BarChartCell(value: 0, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient.greenRed, touchLocation: CGFloat())
|
||||
|
||||
BarChartCell(value: 1, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient.greenRed, touchLocation: CGFloat())
|
||||
BarChartCell(value: 1, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient.whiteBlack, touchLocation: CGFloat())
|
||||
BarChartCell(value: 1, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient(.purple), touchLocation: CGFloat())
|
||||
BarChartCell(value: 1, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient.greenRed, touchLocation: .constant(CGFloat()))
|
||||
BarChartCell(value: 1, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient.whiteBlack, touchLocation: .constant(CGFloat()))
|
||||
BarChartCell(value: 1, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient(.purple), touchLocation: .constant(CGFloat()))
|
||||
}
|
||||
|
||||
|
||||
Group {
|
||||
BarChartCell(value: 1, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient.greenRed, touchLocation: CGFloat())
|
||||
BarChartCell(value: 1, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient.whiteBlack, touchLocation: CGFloat())
|
||||
BarChartCell(value: 1, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient(.purple), touchLocation: CGFloat())
|
||||
BarChartCell(value: 1, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient.greenRed, touchLocation: .constant(CGFloat()))
|
||||
BarChartCell(value: 1, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient.whiteBlack, touchLocation: .constant(CGFloat()))
|
||||
BarChartCell(value: 1, width: 50, numberOfDataPoints: 1, gradientColor: ColorGradient(.purple), touchLocation: .constant(CGFloat()))
|
||||
}.environment(\.colorScheme, .dark)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct BarChartRow: View {
|
||||
@EnvironmentObject var chartValue: ChartValue
|
||||
@ObservedObject var chartData: ChartData
|
||||
@State var data: [Double] = []
|
||||
@State var touchLocation: CGFloat = -1.0
|
||||
|
||||
enum Constant {
|
||||
@@ -12,7 +11,7 @@ public struct BarChartRow: View {
|
||||
var style: ChartStyle
|
||||
|
||||
var maxValue: Double {
|
||||
guard let max = chartData.data.max() else {
|
||||
guard let max = data.max() else {
|
||||
return 1
|
||||
}
|
||||
return max != 0 ? max : 1
|
||||
@@ -21,31 +20,25 @@ public struct BarChartRow: View {
|
||||
public var body: some View {
|
||||
GeometryReader { geometry in
|
||||
HStack(alignment: .bottom,
|
||||
spacing: (geometry.frame(in: .local).width - Constant.spacing) / CGFloat(self.chartData.data.count * 3)) {
|
||||
ForEach(0..<self.chartData.data.count, id: \.self) { index in
|
||||
BarChartCell(value: self.normalizedValue(index: index),
|
||||
index: index,
|
||||
width: Float(geometry.frame(in: .local).width - Constant.spacing),
|
||||
numberOfDataPoints: self.chartData.data.count,
|
||||
gradientColor: self.style.foregroundColor.rotate(for: index),
|
||||
touchLocation: self.touchLocation)
|
||||
.scaleEffect(self.getScaleSize(touchLocation: self.touchLocation, index: index), anchor: .bottom)
|
||||
.animation(Animation.easeIn(duration: 0.2))
|
||||
}
|
||||
// .drawingGroup()
|
||||
spacing: (geometry.frame(in: .local).width - Constant.spacing) / CGFloat(self.data.count * 3)) {
|
||||
ForEach(0..<self.data.count, id: \.self) { index in
|
||||
BarChartCell(value: self.normalizedValue(index: index),
|
||||
index: index,
|
||||
width: Float(geometry.frame(in: .local).width - Constant.spacing),
|
||||
numberOfDataPoints: self.data.count,
|
||||
gradientColor: self.style.foregroundColor.rotate(for: index),
|
||||
touchLocation: self.$touchLocation)
|
||||
.scaleEffect(self.getScaleSize(touchLocation: self.touchLocation, index: index), anchor: .bottom)
|
||||
.animation(.spring())
|
||||
|
||||
}
|
||||
}
|
||||
.padding([.top, .leading, .trailing], 10)
|
||||
.gesture(DragGesture()
|
||||
.onChanged({ value in
|
||||
let width = geometry.frame(in: .local).width
|
||||
self.touchLocation = value.location.x/width
|
||||
if let currentValue = self.getCurrentValue(width: width) {
|
||||
self.chartValue.currentValue = currentValue
|
||||
self.chartValue.interactionInProgress = true
|
||||
}
|
||||
self.touchLocation = value.location.x/geometry.frame(in: .local).width
|
||||
})
|
||||
.onEnded({ value in
|
||||
self.chartValue.interactionInProgress = false
|
||||
self.touchLocation = -1
|
||||
})
|
||||
)
|
||||
@@ -53,20 +46,37 @@ public struct BarChartRow: View {
|
||||
}
|
||||
|
||||
func normalizedValue(index: Int) -> Double {
|
||||
return Double(chartData.data[index])/Double(maxValue)
|
||||
return Double(data[index])/Double(maxValue)
|
||||
}
|
||||
|
||||
func getScaleSize(touchLocation: CGFloat, index: Int) -> CGSize {
|
||||
if touchLocation > CGFloat(index)/CGFloat(chartData.data.count) &&
|
||||
touchLocation < CGFloat(index+1)/CGFloat(chartData.data.count) {
|
||||
if touchLocation > CGFloat(index)/CGFloat(self.data.count) &&
|
||||
touchLocation < CGFloat(index+1)/CGFloat(self.data.count) {
|
||||
return CGSize(width: 1.4, height: 1.1)
|
||||
}
|
||||
return CGSize(width: 1, height: 1)
|
||||
}
|
||||
|
||||
func getCurrentValue(width: CGFloat) -> Double? {
|
||||
guard self.chartData.data.count > 0 else { return nil}
|
||||
let index = max(0,min(self.chartData.data.count-1,Int(floor((self.touchLocation*width)/(width/CGFloat(self.chartData.data.count))))))
|
||||
return self.chartData.data[index]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct BarChartRow_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
BarChartRow(data: [0], style: styleGreenRed)
|
||||
Group {
|
||||
BarChartRow(data: [1, 2, 3], style: styleGreenRed)
|
||||
BarChartRow(data: [1, 2, 3], style: styleGreenRedWhiteBlack)
|
||||
}
|
||||
Group {
|
||||
BarChartRow(data: [1, 2, 3], style: styleGreenRed)
|
||||
BarChartRow(data: [1, 2, 3], style: styleGreenRedWhiteBlack)
|
||||
}.environment(\.colorScheme, .dark)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let styleGreenRed = ChartStyle(backgroundColor: .white, foregroundColor: .greenRed)
|
||||
|
||||
private let styleGreenRedWhiteBlack = ChartStyle(
|
||||
backgroundColor: ColorGradient.init(.white),
|
||||
foregroundColor: [ColorGradient.redBlack, ColorGradient.whiteBlack])
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct Line: View {
|
||||
@EnvironmentObject var chartValue: ChartValue
|
||||
@State var frame: CGRect = .zero
|
||||
@ObservedObject var chartData: ChartData
|
||||
|
||||
@State var data: [Double]
|
||||
var style: ChartStyle
|
||||
|
||||
@State var showIndicator: Bool = false
|
||||
@@ -13,11 +11,11 @@ public struct Line: View {
|
||||
@State var showBackground: Bool = true
|
||||
var curvedLines: Bool = true
|
||||
var step: CGPoint {
|
||||
return CGPoint.getStep(frame: frame, data: chartData.data)
|
||||
return CGPoint.getStep(frame: frame, data: data)
|
||||
}
|
||||
|
||||
var path: Path {
|
||||
let points = chartData.data
|
||||
let points = data
|
||||
|
||||
if curvedLines {
|
||||
return Path.quadCurvedPathWithPoints(points: points,
|
||||
@@ -29,7 +27,7 @@ public struct Line: View {
|
||||
}
|
||||
|
||||
var closedPath: Path {
|
||||
let points = chartData.data
|
||||
let points = data
|
||||
|
||||
if curvedLines {
|
||||
return Path.quadClosedCurvedPathWithPoints(points: points,
|
||||
@@ -61,13 +59,10 @@ public struct Line: View {
|
||||
.onChanged({ value in
|
||||
self.touchLocation = value.location
|
||||
self.showIndicator = true
|
||||
self.getClosestDataPoint(point: self.getClosestPointOnPath(touchLocation: value.location))
|
||||
self.chartValue.interactionInProgress = true
|
||||
})
|
||||
.onEnded({ value in
|
||||
self.touchLocation = .zero
|
||||
self.showIndicator = false
|
||||
self.chartValue.interactionInProgress = false
|
||||
})
|
||||
)
|
||||
}
|
||||
@@ -82,24 +77,11 @@ extension Line {
|
||||
return closest
|
||||
}
|
||||
|
||||
private func getClosestDataPoint(point: CGPoint) {
|
||||
let index = Int(round((point.x)/step.x))
|
||||
if (index >= 0 && index < self.chartData.data.count){
|
||||
self.chartValue.currentValue = self.chartData.data[index]
|
||||
}
|
||||
}
|
||||
|
||||
private func getBackgroundPathView() -> some View {
|
||||
self.closedPath
|
||||
.fill(LinearGradient(gradient: Gradient(colors: [
|
||||
style.foregroundColor.first?.startColor ?? .white,
|
||||
style.foregroundColor.first?.endColor ?? .white,
|
||||
.white]),
|
||||
startPoint: .bottom,
|
||||
endPoint: .top))
|
||||
.fill(style.backgroundColor.linearGradient(from: .bottom, to: .top))
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
.opacity(0.2)
|
||||
.transition(.opacity)
|
||||
.animation(.easeIn(duration: 1.6))
|
||||
}
|
||||
@@ -127,8 +109,8 @@ extension Line {
|
||||
struct Line_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
Line(chartData: ChartData([8, 23, 32, 7, 23, 43]), style: blackLineStyle)
|
||||
Line(chartData: ChartData([8, 23, 32, 7, 23, 43]), style: redLineStyle)
|
||||
Line(data: [1, 2, 3, 1, 2, 5, 7], style: blackLineStyle)
|
||||
Line(data: [1, 2, 3, 1, 2, 5, 7], style: redLineStyle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,32 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct LineChart: View, ChartBase {
|
||||
public var chartData = ChartData()
|
||||
|
||||
@EnvironmentObject var data: ChartData
|
||||
@EnvironmentObject var style: ChartStyle
|
||||
|
||||
public var body: some View {
|
||||
Line(chartData: data, style: style)
|
||||
public struct LineChart: ChartType {
|
||||
public func makeChart(configuration: Self.Configuration, style: Self.Style) -> some View {
|
||||
Line(data: configuration.data, style: style)
|
||||
}
|
||||
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
struct LineChart_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
LineChart().makeChart(
|
||||
configuration: .init(data: [0]),
|
||||
style: .init(backgroundColor: .white, foregroundColor: ColorGradient(.black)))
|
||||
Group {
|
||||
LineChart().makeChart(
|
||||
configuration: .init(data: [1, 2, 3, 5, 1]),
|
||||
style: .init(backgroundColor: .white, foregroundColor: ColorGradient(.black)))
|
||||
}.environment(\.colorScheme, .light)
|
||||
|
||||
Group {
|
||||
LineChart().makeChart(
|
||||
configuration: .init(data: [1, 2, 3]),
|
||||
style: .init(backgroundColor: .white, foregroundColor: ColorGradient.redBlack))
|
||||
}.environment(\.colorScheme, .dark)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,63 @@
|
||||
//
|
||||
// PieChart.swift
|
||||
// SwiftUICharts
|
||||
//
|
||||
// Created by Nicolas Savoini on 2020-05-24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
public struct PieChart: View, ChartBase {
|
||||
public var chartData = ChartData()
|
||||
|
||||
@EnvironmentObject var data: ChartData
|
||||
@EnvironmentObject var style: ChartStyle
|
||||
|
||||
public var body: some View {
|
||||
PieChartRow(chartData: data, style: style)
|
||||
public struct PieChart: ChartType {
|
||||
public func makeChart(configuration: Self.Configuration, style: Self.Style) -> some View {
|
||||
PieChartRow(data: configuration.data, style: style)
|
||||
}
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
struct PieChart_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
PieChart().makeChart(
|
||||
configuration: .init(data: [0]),
|
||||
style: styleOneColor)
|
||||
|
||||
Group {
|
||||
PieChart().makeChart(
|
||||
configuration: .init(data: [56, 78, 53, 65, 54]),
|
||||
style: styleOneColor)
|
||||
PieChart().makeChart(
|
||||
configuration: .init(data: [56, 78, 53, 65, 54]),
|
||||
style: styleTwoColor)
|
||||
PieChart().makeChart(
|
||||
configuration: .init(data: [1, 1, 1, 1, 1, 1]),
|
||||
style: trivialPursuit)
|
||||
}.environment(\.colorScheme, .light)
|
||||
|
||||
Group {
|
||||
PieChart().makeChart(
|
||||
configuration: .init(data: [56, 78, 53, 65, 54]),
|
||||
style: styleOneColor)
|
||||
PieChart().makeChart(
|
||||
configuration: .init(data: [56, 78, 53, 65, 54]),
|
||||
style: styleTwoColor)
|
||||
PieChart().makeChart(
|
||||
configuration: .init(data: [1, 1, 1, 1, 1, 1]),
|
||||
style: trivialPursuit)
|
||||
}.environment(\.colorScheme, .dark)
|
||||
|
||||
}.previewLayout(.fixed(width: 250, height: 400))
|
||||
}
|
||||
}
|
||||
|
||||
private let styleOneColor = ChartStyle(backgroundColor: .white, foregroundColor: ColorGradient.init(.pink))
|
||||
|
||||
private let styleTwoColor = ChartStyle(backgroundColor: ColorGradient(.black), foregroundColor: [ColorGradient(.yellow), ColorGradient(.red)])
|
||||
|
||||
private let trivialPursuit = ChartStyle(
|
||||
backgroundColor: .yellow,
|
||||
foregroundColor: [ColorGradient(.yellow),
|
||||
ColorGradient(.pink),
|
||||
ColorGradient(.green),
|
||||
ColorGradient(.primary),
|
||||
ColorGradient(.blue),
|
||||
ColorGradient(.orange)])
|
||||
|
||||
@@ -8,16 +8,16 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct PieChartRow: View {
|
||||
@ObservedObject var chartData: ChartData
|
||||
|
||||
var data: [Double]
|
||||
|
||||
var style: ChartStyle
|
||||
|
||||
var slices: [PieSlice] {
|
||||
var tempSlices: [PieSlice] = []
|
||||
var lastEndDeg: Double = 0
|
||||
let maxValue: Double = chartData.data.reduce(0, +)
|
||||
let maxValue: Double = data.reduce(0, +)
|
||||
|
||||
for slice in chartData.data {
|
||||
for slice in data {
|
||||
let normalized: Double = Double(slice) / (maxValue == 0 ? 1 : maxValue)
|
||||
let startDeg = lastEndDeg
|
||||
let endDeg = lastEndDeg + (normalized * 360)
|
||||
@@ -53,17 +53,17 @@ struct PieChartRow_Previews: PreviewProvider {
|
||||
Group {
|
||||
//Empty Array - Default Colors.OrangeStart
|
||||
PieChartRow(
|
||||
chartData: ChartData([8, 23, 32, 7, 23, 43]),
|
||||
data: [8, 23, 32, 7, 23, 43],
|
||||
style: defaultMultiColorChartStyle)
|
||||
.frame(width: 100, height: 100)
|
||||
|
||||
PieChartRow(
|
||||
chartData: ChartData([8, 23, 32, 7, 23, 43]),
|
||||
data: [8, 23, 32, 7, 23, 43],
|
||||
style: multiColorChartStyle)
|
||||
.frame(width: 100, height: 100)
|
||||
|
||||
PieChartRow(
|
||||
chartData: ChartData([8, 23, 32, 7, 23, 43]),
|
||||
data: [0],
|
||||
style: multiColorChartStyle)
|
||||
.frame(width: 100, height: 100)
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import SwiftUI
|
||||
|
||||
extension EnvironmentValues {
|
||||
var chartType: AnyChartType {
|
||||
get {
|
||||
return self[ChartTypeKey.self]
|
||||
}
|
||||
set {
|
||||
self[ChartTypeKey.self] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var chartStyle: ChartStyle {
|
||||
get {
|
||||
return self[ChartStyleKey.self]
|
||||
}
|
||||
set {
|
||||
self[ChartStyleKey.self] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ChartTypeKey: EnvironmentKey {
|
||||
static let defaultValue: AnyChartType = AnyChartType(BarChart())
|
||||
}
|
||||
|
||||
struct ChartStyleKey: EnvironmentKey {
|
||||
static let defaultValue: ChartStyle = ChartStyle(backgroundColor: .white,
|
||||
foregroundColor: ColorGradient(ChartColors.orangeDark,
|
||||
ChartColors.orangeBright))
|
||||
}
|
||||
Reference in New Issue
Block a user