Compare commits

...

4 Commits

Author SHA1 Message Date
Andras Samu 841bde1377 Fixed infinite size compile error 2020-02-13 12:15:28 +01:00
Andras Samu 37779e1b54 added a darkmodestyle so you can customize darkmode appearance for lineview, linechartview, barchartview 2020-02-13 11:59:21 +01:00
Andras Samu ba5bc4f861 Fixed barchart crashing for empty array 2020-02-13 11:31:49 +01:00
Andras Samu 80d546de03 fixed lineview for small negative numbers 2020-02-13 11:20:39 +01:00
5 changed files with 99 additions and 42 deletions
@@ -9,15 +9,17 @@
import SwiftUI
public struct BarChartView : View {
@Environment(\.colorScheme) var colorScheme: ColorScheme
private var data: ChartData
public var title: String
public var legend: String?
public var style: ChartStyle
public var darkModeStyle: ChartStyle
public var formSize:CGSize
public var dropShadow: Bool
public var cornerImage: Image
public var valueSpecifier:String
@State private var touchLocation: CGFloat = -1.0
@State private var showValue: Bool = false
@State private var showLabelValue: Bool = false
@@ -36,6 +38,7 @@ public struct BarChartView : View {
self.title = title
self.legend = legend
self.style = style
self.darkModeStyle = style.darkModeStyle != nil ? style.darkModeStyle! : Styles.barChartStyleOrangeDark
self.formSize = form!
self.dropShadow = dropShadow!
self.cornerImage = cornerImage!
@@ -45,7 +48,7 @@ public struct BarChartView : View {
public var body: some View {
ZStack{
Rectangle()
.fill(self.style.backgroundColor)
.fill(self.colorScheme == .dark ? self.darkModeStyle.backgroundColor : self.style.backgroundColor)
.cornerRadius(20)
.shadow(color: Color.gray, radius: self.dropShadow ? 8 : 0)
VStack(alignment: .leading){
@@ -53,41 +56,48 @@ public struct BarChartView : View {
if(!showValue){
Text(self.title)
.font(.headline)
.foregroundColor(self.style.textColor)
.foregroundColor(self.colorScheme == .dark ? self.darkModeStyle.textColor : self.style.textColor)
}else{
Text("\(self.currentValue, specifier: self.valueSpecifier)")
.font(.headline)
.foregroundColor(self.style.textColor)
.foregroundColor(self.colorScheme == .dark ? self.darkModeStyle.textColor : self.style.textColor)
}
if(self.formSize == ChartForm.large && self.legend != nil && !showValue) {
Text(self.legend!)
.font(.callout)
.foregroundColor(self.style.accentColor)
.foregroundColor(self.colorScheme == .dark ? self.darkModeStyle.accentColor : self.style.accentColor)
.transition(.opacity)
.animation(.easeOut)
}
Spacer()
self.cornerImage
.imageScale(.large)
.foregroundColor(self.style.legendTextColor)
.foregroundColor(self.colorScheme == .dark ? self.darkModeStyle.legendTextColor : self.style.legendTextColor)
}.padding()
BarChartRow(data: data.points.map{$0.1}, accentColor: self.style.accentColor, secondGradientAccentColor: self.style.secondGradientColor, touchLocation: self.$touchLocation)
BarChartRow(data: data.points.map{$0.1},
accentColor: self.colorScheme == .dark ? self.darkModeStyle.accentColor : self.style.accentColor,
secondGradientAccentColor: self.colorScheme == .dark ? self.darkModeStyle.secondGradientColor : self.style.secondGradientColor,
touchLocation: self.$touchLocation)
if self.legend != nil && self.formSize == ChartForm.medium && !self.showLabelValue{
Text(self.legend!)
.font(.headline)
.foregroundColor(self.style.legendTextColor)
.foregroundColor(self.colorScheme == .dark ? self.darkModeStyle.legendTextColor : self.style.legendTextColor)
.padding()
}else if (self.data.valuesGiven) {
LabelView(arrowOffset: self.getArrowOffset(touchLocation: self.touchLocation), title: .constant(self.getCurrentValue().0)).offset(x: self.getLabelViewOffset(touchLocation: self.touchLocation), y: -6)
}else if (self.data.valuesGiven && self.getCurrentValue() != nil) {
LabelView(arrowOffset: self.getArrowOffset(touchLocation: self.touchLocation),
title: .constant(self.getCurrentValue()!.0)).offset(x: self.getLabelViewOffset(touchLocation: self.touchLocation), y: -6)
}
}
}.frame(minWidth:self.formSize.width, maxWidth: self.isFullWidth ? .infinity : self.formSize.width, minHeight:self.formSize.height, maxHeight:self.formSize.height)
}.frame(minWidth:self.formSize.width,
maxWidth: self.isFullWidth ? .infinity : self.formSize.width,
minHeight:self.formSize.height,
maxHeight:self.formSize.height)
.gesture(DragGesture()
.onChanged({ value in
self.touchLocation = value.location.x/self.formSize.width
self.showValue = true
self.currentValue = self.getCurrentValue().1
self.currentValue = self.getCurrentValue()?.1 ?? 0
if(self.data.valuesGiven && self.formSize == ChartForm.medium) {
self.showLabelValue = true
}
@@ -117,7 +127,8 @@ public struct BarChartView : View {
return min(self.formSize.width-110,max(10,(self.touchLocation * self.formSize.width) - 50))
}
func getCurrentValue()-> (String,Double) {
func getCurrentValue() -> (String,Double)? {
guard self.data.points.count > 0 else { return nil}
let index = max(0,min(self.data.points.count-1,Int(floor((self.touchLocation*self.formSize.width)/(self.formSize.width/CGFloat(self.data.points.count))))))
return self.data.points[index]
}
@@ -126,7 +137,10 @@ public struct BarChartView : View {
#if DEBUG
struct ChartView_Previews : PreviewProvider {
static var previews: some View {
BarChartView(data: TestData.values ,title: "Model 3 sales", legend: "Quarterly", valueSpecifier: "%.0f")
BarChartView(data: TestData.values ,
title: "Model 3 sales",
legend: "Quarterly",
valueSpecifier: "%.0f")
}
}
#endif
+15 -7
View File
@@ -86,6 +86,13 @@ public struct Styles {
secondGradientColor: Colors.OrangeEnd,
textColor: Color.black,
legendTextColor: Color.gray)
public static let lineViewDarkMode = ChartStyle(
backgroundColor: Color.black,
accentColor: Colors.OrangeStart,
secondGradientColor: Colors.OrangeEnd,
textColor: Color.white,
legendTextColor: Color.white)
}
public struct ChartForm {
@@ -104,12 +111,13 @@ public struct ChartForm {
}
public struct ChartStyle {
public class ChartStyle {
public var backgroundColor: Color
public var accentColor: Color
public var secondGradientColor: Color
public var textColor: Color
public var legendTextColor: Color
public weak var darkModeStyle: ChartStyle?
public init(backgroundColor: Color, accentColor: Color, secondGradientColor: Color, textColor: Color, legendTextColor: Color){
self.backgroundColor = backgroundColor
@@ -131,7 +139,7 @@ public struct ChartStyle {
public class ChartData: ObservableObject {
@Published var points: [(String,Double)]
var valuesGiven: Bool = false
public init<N: BinaryFloatingPoint>(points:[N]) {
self.points = points.map{("", Double($0))}
}
@@ -160,11 +168,11 @@ public class ChartData: ObservableObject {
public class TestData{
static public var data:ChartData = ChartData(points: [37,72,51,22,39,47,66,85,50])
static public var values:ChartData = ChartData(values: [("2017 Q3",220),
("2017 Q4",1550),
("2018 Q1",8180),
("2018 Q2",18440),
("2018 Q3",55840),
("2018 Q4",63150), ("2019 Q1",50900), ("2019 Q2",77550), ("2019 Q3",79600), ("2019 Q4",92550)])
("2017 Q4",1550),
("2018 Q1",8180),
("2018 Q2",18440),
("2018 Q3",55840),
("2018 Q4",63150), ("2019 Q1",50900), ("2019 Q2",77550), ("2019 Q3",79600), ("2019 Q4",92550)])
}
+2 -2
View File
@@ -116,7 +116,7 @@ extension Path {
return path
}
guard var offset = points.min() else { return path }
offset -= 3
// offset -= 3
var p1 = CGPoint(x: 0, y: CGFloat(points[0]-offset)*step.y)
path.move(to: p1)
for pointIndex in 1..<points.count {
@@ -135,7 +135,7 @@ extension Path {
return path
}
guard var offset = points.min() else { return path }
offset -= 3
// offset -= 3
path.move(to: .zero)
var p1 = CGPoint(x: 0, y: CGFloat(points[0]-offset)*step.y)
path.addLine(to: p1)
@@ -9,11 +9,13 @@
import SwiftUI
public struct LineChartView: View {
// let selectionFeedbackGenerator = UISelectionFeedbackGenerator()
@Environment(\.colorScheme) var colorScheme: ColorScheme
@ObservedObject var data:ChartData
public var title: String
public var legend: String?
public var style: ChartStyle
public var darkModeStyle: ChartStyle
public var formSize:CGSize
public var dropShadow: Bool
public var valueSpecifier:String
@@ -23,7 +25,6 @@ public struct LineChartView: View {
@State private var currentValue: Double = 2 {
didSet{
if (oldValue != self.currentValue && showIndicatorDot) {
// selectionFeedbackGenerator.selectionChanged()
HapticFeedback.playSelection()
}
@@ -32,11 +33,20 @@ public struct LineChartView: View {
let frame = CGSize(width: 180, height: 120)
private var rateValue: Int
public init(data: [Double], title: String, legend: String? = nil, style: ChartStyle = Styles.lineChartStyleOne, form: CGSize? = ChartForm.medium ,rateValue: Int? = 14, dropShadow: Bool? = true, valueSpecifier: String? = "%.1f"){
public init(data: [Double],
title: String,
legend: String? = nil,
style: ChartStyle = Styles.lineChartStyleOne,
form: CGSize? = ChartForm.medium,
rateValue: Int? = 14,
dropShadow: Bool? = true,
valueSpecifier: String? = "%.1f") {
self.data = ChartData(points: data)
self.title = title
self.legend = legend
self.style = style
self.darkModeStyle = style.darkModeStyle != nil ? style.darkModeStyle! : Styles.lineViewDarkMode
self.formSize = form!
self.rateValue = rateValue!
self.dropShadow = dropShadow!
@@ -45,13 +55,21 @@ public struct LineChartView: View {
public var body: some View {
ZStack(alignment: .center){
RoundedRectangle(cornerRadius: 20).fill(self.style.backgroundColor).frame(width: frame.width, height: 240, alignment: .center).shadow(radius: self.dropShadow ? 8 : 0)
RoundedRectangle(cornerRadius: 20)
.fill(self.colorScheme == .dark ? self.darkModeStyle.backgroundColor : self.style.backgroundColor)
.frame(width: frame.width, height: 240, alignment: .center)
.shadow(radius: self.dropShadow ? 8 : 0)
VStack(alignment: .leading){
if(!self.showIndicatorDot){
VStack(alignment: .leading, spacing: 8){
Text(self.title).font(.title).bold().foregroundColor(self.style.textColor)
Text(self.title)
.font(.title)
.bold()
.foregroundColor(self.colorScheme == .dark ? self.darkModeStyle.textColor : self.style.textColor)
if (self.legend != nil){
Text(self.legend!).font(.callout).foregroundColor(self.style.legendTextColor)
Text(self.legend!)
.font(.callout)
.foregroundColor(self.colorScheme == .dark ? self.darkModeStyle.legendTextColor :self.style.legendTextColor)
}
HStack {
if (self.rateValue >= 0){
@@ -74,12 +92,12 @@ public struct LineChartView: View {
Spacer()
}
.transition(.scale)
// .animation(.spring())
}
Spacer()
GeometryReader{ geometry in
Line(data: self.data, frame: .constant(geometry.frame(in: .local)), touchLocation: self.$touchLocation, showIndicator: self.$showIndicatorDot)
Line(data: self.data,
frame: .constant(geometry.frame(in: .local)),
touchLocation: self.$touchLocation, showIndicator: self.$showIndicatorDot)
}
.frame(width: frame.width, height: frame.height + 30)
.clipShape(RoundedRectangle(cornerRadius: 20))
+27 -10
View File
@@ -13,6 +13,7 @@ public struct LineView: View {
public var title: String?
public var legend: String?
public var style: ChartStyle
public var darkModeStyle: ChartStyle
public var valueSpecifier:String
@Environment(\.colorScheme) var colorScheme: ColorScheme
@@ -24,12 +25,18 @@ public struct LineView: View {
@State private var currentDataNumber: Double = 0
@State private var hideHorizontalLines: Bool = false
public init(data: [Double], title: String? = nil, legend: String? = nil, style: ChartStyle? = Styles.lineChartStyleOne, valueSpecifier: String? = "%.1f"){
public init(data: [Double],
title: String? = nil,
legend: String? = nil,
style: ChartStyle = Styles.lineChartStyleOne,
valueSpecifier: String? = "%.1f") {
self.data = ChartData(points: data)
self.title = title
self.legend = legend
self.style = style!
self.style = style
self.valueSpecifier = valueSpecifier!
self.darkModeStyle = style.darkModeStyle != nil ? style.darkModeStyle! : Styles.lineViewDarkMode
}
public var body: some View {
@@ -37,25 +44,36 @@ public struct LineView: View {
VStack(alignment: .leading, spacing: 8) {
Group{
if (self.title != nil){
Text(self.title!).font(.title).bold().foregroundColor(self.colorScheme == .dark ? Color.white : self.style.textColor)
Text(self.title!)
.font(.title)
.bold().foregroundColor(self.colorScheme == .dark ? self.darkModeStyle.textColor : self.style.textColor)
}
if (self.legend != nil){
Text(self.legend!).font(.callout).foregroundColor(self.colorScheme == .dark ? Color.white : self.style.legendTextColor)
Text(self.legend!)
.font(.callout)
.foregroundColor(self.colorScheme == .dark ? self.darkModeStyle.legendTextColor : self.style.legendTextColor)
}
}.offset(x: 0, y: 20)
ZStack{
GeometryReader{ reader in
Rectangle().foregroundColor(self.colorScheme == .dark ? Color.black : self.style.backgroundColor)
Rectangle()
.foregroundColor(self.colorScheme == .dark ? self.darkModeStyle.backgroundColor : self.style.backgroundColor)
if(self.showLegend){
Legend(data: self.data, frame: .constant(reader.frame(in: .local)), hideHorizontalLines: self.$hideHorizontalLines)
Legend(data: self.data,
frame: .constant(reader.frame(in: .local)), hideHorizontalLines: self.$hideHorizontalLines)
.transition(.opacity)
.animation(Animation.easeOut(duration: 1).delay(1))
}
Line(data: self.data, frame: .constant(CGRect(x: 0, y: 0, width: reader.frame(in: .local).width - 30, height: reader.frame(in: .local).height)), touchLocation: self.$indicatorLocation,showIndicator: self.$hideHorizontalLines ,showBackground: false).offset(x: 30, y: 0)
Line(data: self.data,
frame: .constant(CGRect(x: 0, y: 0, width: reader.frame(in: .local).width - 30, height: reader.frame(in: .local).height)),
touchLocation: self.$indicatorLocation,showIndicator: self.$hideHorizontalLines ,showBackground: false)
.offset(x: 30, y: 0)
.onAppear(){
self.showLegend.toggle()
}
}.frame(width: geometry.frame(in: .local).size.width, height: 240).offset(x: 0, y: 40 )
}
.frame(width: geometry.frame(in: .local).size.width, height: 240)
.offset(x: 0, y: 40 )
MagnifierRect(currentNumber: self.$currentDataNumber, valueSpecifier: self.valueSpecifier)
.opacity(self.opacity)
.offset(x: self.dragLocation.x - geometry.frame(in: .local).size.width/2, y: 36)
@@ -115,7 +133,6 @@ struct MagnifierRect: View {
Text("\(self.currentNumber, specifier: valueSpecifier)")
.font(.system(size: 18, weight: .bold))
.offset(x: 0, y:-110)
// .animation(.spring())
.foregroundColor(self.colorScheme == .dark ? Color.white : Color.black)
if (self.colorScheme == .dark ){
RoundedRectangle(cornerRadius: 16)
@@ -131,6 +148,6 @@ struct MagnifierRect: View {
}
}//.animation(.linear)
}
}
}