Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 04b6e385ea | |||
| 524aec2a04 | |||
| 03f90728b4 | |||
| 0d95dbd3d4 | |||
| fd14ca2327 | |||
| 20fb782a3e | |||
| 9fa7e20221 | |||
| b5e3aa897c | |||
| 1b45a6a922 | |||
| d3d0b086f1 | |||
| 6cc43d9dc0 | |||
| e081d3a88d |
Generated
BIN
Binary file not shown.
@@ -26,6 +26,17 @@ You can display a Chart by adding a chart view to your parent view:
|
||||
Added an example project, with **iOS, watchOS** target: https://github.com/AppPear/ChartViewDemo
|
||||
|
||||
## Line charts
|
||||
|
||||
**New full screen view called LineView!!!**
|
||||
|
||||

|
||||
|
||||
```swift
|
||||
LineView(data: [8,23,54,32,12,37,7,23,43], title: "Line chart", legend: "Full screen") // legend is optional, use optional .padding()
|
||||
```
|
||||
|
||||
Adopts to dark mode automatically
|
||||
|
||||

|
||||
|
||||
**Line chart is interactive, so you can drag across to reveal the data points**
|
||||
@@ -36,6 +47,8 @@ You can add a line chart with the following code:
|
||||
LineChartView(data: [8,23,54,32,12,37,7,23,43], title: "Title", legend: "Legendary") // legend is optional
|
||||
```
|
||||
|
||||
**Turn drop shadow off by adding to the Initialiser: `dropShadow: false`**
|
||||
|
||||
|
||||
## Bar charts
|
||||

|
||||
@@ -49,14 +62,15 @@ You can add a bar chart with the following code:
|
||||
```
|
||||
|
||||
You can add different formats:
|
||||
* Small `Form.small`
|
||||
* Medium `Form.medium`
|
||||
* Large `Form.large`
|
||||
* Small `ChartForm.small`
|
||||
* Medium `ChartForm.medium`
|
||||
* Large `ChartForm.large`
|
||||
|
||||
```swift
|
||||
BarChartView(data: [8,23,54,32,12,37,7,23,43], title: "Title", form: Form.small)
|
||||
BarChartView(data: [8,23,54,32,12,37,7,23,43], title: "Title", form: ChartForm.small)
|
||||
```
|
||||
|
||||
**Turn drop shadow off by adding to the Initialiser: `dropShadow: false`**
|
||||
|
||||
### You can customize styling of the chart with a ChartStyle object:
|
||||
|
||||
Customizable:
|
||||
@@ -67,7 +81,7 @@ Customizable:
|
||||
* legend text color
|
||||
|
||||
```swift
|
||||
let chartStyle = ChartStyle(backgroundColor: Color.black, accentColor: Colors.OrangeStart, secondGradientColor: Colors.OrangeEnd, chartFormSize: Form.medium, textColor: Color.white, legendTextColor: Color.white )
|
||||
let chartStyle = ChartStyle(backgroundColor: Color.black, accentColor: Colors.OrangeStart, secondGradientColor: Colors.OrangeEnd, chartFormSize: ChartForm.medium, textColor: Color.white, legendTextColor: Color.white )
|
||||
...
|
||||
BarChartView(data: [8,23,54,32,12,37,7,23,43], title: "Title", style: chartStyle)
|
||||
```
|
||||
@@ -89,16 +103,16 @@ You can access built-in styles:
|
||||

|
||||
|
||||
|
||||
### You can customize the size of the chart with a Form object:
|
||||
### You can customize the size of the chart with a ChartForm object:
|
||||
|
||||
**Form**
|
||||
**ChartForm**
|
||||
* `.small`
|
||||
* `.medium`
|
||||
* `.large`
|
||||
* `.detail`
|
||||
|
||||
```swift
|
||||
BarChartView(data: [8,23,54,32,12,37,7,23,43], title: "Title", form: Form.small)
|
||||
BarChartView(data: [8,23,54,32,12,37,7,23,43], title: "Title", form: ChartForm.small)
|
||||
```
|
||||
|
||||
### WatchOS support for Bar charts:
|
||||
@@ -114,3 +128,5 @@ You can add a line chart with the following code:
|
||||
PieChartView(data: [8,23,54,32], title: "Title", legend: "Legendary") // legend is optional
|
||||
```
|
||||
|
||||
**Turn drop shadow off by adding to the Initialiser: `dropShadow: false`**
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct BarChartRow : View {
|
||||
var data: [Int]
|
||||
var data: [Double]
|
||||
var accentColor: Color
|
||||
var secondGradientAccentColor: Color?
|
||||
var maxValue: Int {
|
||||
var maxValue: Double {
|
||||
data.max() ?? 0
|
||||
}
|
||||
@Binding var touchLocation: CGFloat
|
||||
@@ -22,6 +22,7 @@ public struct BarChartRow : View {
|
||||
ForEach(0..<self.data.count) { i in
|
||||
BarChartCell(value: self.normalizedValue(index: i), index: i, width: Float(geometry.frame(in: .local).width - 22), numberOfDataPoints: self.data.count, accentColor: self.accentColor, secondGradientAccentColor: self.secondGradientAccentColor, touchLocation: self.$touchLocation)
|
||||
.scaleEffect(self.touchLocation > CGFloat(i)/CGFloat(self.data.count) && self.touchLocation < CGFloat(i+1)/CGFloat(self.data.count) ? CGSize(width: 1.4, height: 1.1) : CGSize(width: 1, height: 1), anchor: .bottom)
|
||||
.animation(.spring())
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct BarChartView : View {
|
||||
public var data: [Int]
|
||||
public var data: [Double]
|
||||
public var title: String
|
||||
public var legend: String?
|
||||
public var style: ChartStyle
|
||||
@@ -20,7 +20,7 @@ public struct BarChartView : View {
|
||||
|
||||
@State private var touchLocation: CGFloat = -1.0
|
||||
@State private var showValue: Bool = false
|
||||
@State private var currentValue: Int = 0 {
|
||||
@State private var currentValue: Double = 0 {
|
||||
didSet{
|
||||
if(oldValue != self.currentValue && self.showValue) {
|
||||
// selectionFeedbackGenerator.selectionChanged()
|
||||
@@ -29,9 +29,9 @@ public struct BarChartView : View {
|
||||
}
|
||||
}
|
||||
var isFullWidth:Bool {
|
||||
return self.formSize == Form.large
|
||||
return self.formSize == ChartForm.large
|
||||
}
|
||||
public init(data: [Int], title: String, legend: String? = nil, style: ChartStyle = Styles.barChartStyleOrangeLight, form: CGSize? = Form.medium, dropShadow: Bool? = true){
|
||||
public init(data: [Double], title: String, legend: String? = nil, style: ChartStyle = Styles.barChartStyleOrangeLight, form: CGSize? = ChartForm.medium, dropShadow: Bool? = true){
|
||||
self.data = data
|
||||
self.title = title
|
||||
self.legend = legend
|
||||
@@ -57,7 +57,7 @@ public struct BarChartView : View {
|
||||
.font(.headline)
|
||||
.foregroundColor(self.style.textColor)
|
||||
}
|
||||
if(self.formSize == Form.large && self.legend != nil && !showValue) {
|
||||
if(self.formSize == ChartForm.large && self.legend != nil && !showValue) {
|
||||
Text(self.legend!)
|
||||
.font(.callout)
|
||||
.foregroundColor(self.style.accentColor)
|
||||
@@ -70,7 +70,7 @@ public struct BarChartView : View {
|
||||
.foregroundColor(self.style.legendTextColor)
|
||||
}.padding()
|
||||
BarChartRow(data: data, accentColor: self.style.accentColor, secondGradientAccentColor: self.style.secondGradientColor, touchLocation: self.$touchLocation)
|
||||
if self.legend != nil && self.formSize == Form.medium {
|
||||
if self.legend != nil && self.formSize == ChartForm.medium {
|
||||
Text(self.legend!)
|
||||
.font(.headline)
|
||||
.foregroundColor(self.style.legendTextColor)
|
||||
@@ -94,7 +94,7 @@ public struct BarChartView : View {
|
||||
)
|
||||
}
|
||||
|
||||
func getCurrentValue()-> Int{
|
||||
func getCurrentValue()-> Double {
|
||||
let index = max(0,min(self.data.count-1,Int(floor((self.touchLocation*self.formSize.width)/(self.formSize.width/CGFloat(self.data.count))))))
|
||||
return self.data[index]
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ public struct Colors {
|
||||
public static let OrangeEnd:Color = Color(hexString: "#EC2301")
|
||||
public static let LegendText:Color = Color(hexString: "#A7A6A8")
|
||||
public static let LegendColor:Color = Color(hexString: "#E8E7EA")
|
||||
public static let LegendDarkColor:Color = Color(hexString: "#545454")
|
||||
public static let IndicatorKnob:Color = Color(hexString: "#FF57A6")
|
||||
public static let GradientUpperBlue:Color = Color(hexString: "#C2E8FF")
|
||||
public static let GradinetUpperBlue1:Color = Color(hexString: "#A8E1FF")
|
||||
@@ -87,7 +88,7 @@ public struct Styles {
|
||||
legendTextColor: Color.gray)
|
||||
}
|
||||
|
||||
public struct Form {
|
||||
public struct ChartForm {
|
||||
#if os(watchOS)
|
||||
public static let small = CGSize(width:120, height:90)
|
||||
public static let medium = CGSize(width:120, height:160)
|
||||
@@ -128,10 +129,10 @@ public struct ChartStyle {
|
||||
}
|
||||
|
||||
class ChartData: ObservableObject {
|
||||
@Published var points: [Int] = [Int]()
|
||||
@Published var currentPoint: Int? = nil
|
||||
@Published var points: [Double] = [Double]()
|
||||
@Published var currentPoint: Double? = nil
|
||||
|
||||
init(points:[Int]) {
|
||||
init(points:[Double]) {
|
||||
self.points = points
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,23 +12,39 @@ struct Legend: View {
|
||||
@ObservedObject var data: ChartData
|
||||
@Binding var frame: CGRect
|
||||
@Binding var hideHorizontalLines: Bool
|
||||
|
||||
@Environment(\.colorScheme) var colorScheme: ColorScheme
|
||||
let padding:CGFloat = 3
|
||||
|
||||
var stepWidth: CGFloat {
|
||||
if data.points.count < 2 {
|
||||
return 0
|
||||
}
|
||||
return frame.size.width / CGFloat(data.points.count-1)
|
||||
}
|
||||
var stepHeight: CGFloat {
|
||||
return frame.size.height / CGFloat(data.points.max()! + data.points.min()!)
|
||||
if let min = data.points.min(), let max = data.points.max(), min != max {
|
||||
if (min < 0){
|
||||
return (frame.size.height-padding) / CGFloat(max - min)
|
||||
}else{
|
||||
return (frame.size.height-padding) / CGFloat(max + min)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var min: CGFloat {
|
||||
return CGFloat(data.points.min() ?? 0)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .topLeading){
|
||||
ForEach((0...4), id: \.self) { height in
|
||||
HStack(alignment: .center){
|
||||
Text("\(self.getYLegend()![height])").offset(x: 0, y: (self.frame.height-CGFloat(self.getYLegend()![height])*self.stepHeight)-(self.frame.height/2))
|
||||
Text("\(self.getYLegendSafe(height: height), specifier: "%.2f")").offset(x: 0, y: self.getYposition(height: height) )
|
||||
.foregroundColor(Colors.LegendText)
|
||||
.font(.caption)
|
||||
self.line(atHeight: CGFloat(self.getYLegend()![height]), width: self.frame.width)
|
||||
.stroke(Colors.LegendColor, style: StrokeStyle(lineWidth: 1.5, lineCap: .round, dash: [5,height == 0 ? 0 : 10]))
|
||||
self.line(atHeight: self.getYLegendSafe(height: height), width: self.frame.width)
|
||||
.stroke(self.colorScheme == .dark ? Colors.LegendDarkColor : Colors.LegendColor, style: StrokeStyle(lineWidth: 1.5, lineCap: .round, dash: [5,height == 0 ? 0 : 10]))
|
||||
.opacity((self.hideHorizontalLines && height != 0) ? 0 : 1)
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
@@ -41,30 +57,40 @@ struct Legend: View {
|
||||
}
|
||||
}
|
||||
|
||||
func getYLegendSafe(height:Int)->CGFloat{
|
||||
if let legend = getYLegend() {
|
||||
return CGFloat(legend[height])
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func getYposition(height: Int)-> CGFloat {
|
||||
if let legend = getYLegend() {
|
||||
return (self.frame.height-((CGFloat(legend[height]) - min)*self.stepHeight))-(self.frame.height/2)
|
||||
}
|
||||
return 0
|
||||
|
||||
}
|
||||
|
||||
func line(atHeight: CGFloat, width: CGFloat) -> Path {
|
||||
var hLine = Path()
|
||||
hLine.move(to: CGPoint(x:5, y: atHeight*stepHeight))
|
||||
hLine.addLine(to: CGPoint(x: width, y: atHeight*stepHeight))
|
||||
hLine.move(to: CGPoint(x:5, y: (atHeight-min)*stepHeight))
|
||||
hLine.addLine(to: CGPoint(x: width, y: (atHeight-min)*stepHeight))
|
||||
return hLine
|
||||
}
|
||||
|
||||
func getYLegend() -> [Int]? {
|
||||
func getYLegend() -> [Double]? {
|
||||
guard let max = data.points.max() else { return nil }
|
||||
guard let min = data.points.min() else { return nil }
|
||||
if(min > 0){
|
||||
let upperBound = ((max/10)+1) * 10
|
||||
let step = upperBound/4
|
||||
return [step * 0,step * 1, step * 2, step * 3, step * 4]
|
||||
}
|
||||
|
||||
return nil
|
||||
let step = Double(max - min)/4
|
||||
return [min+step * 0, min+step * 1, min+step * 2, min+step * 3, min+step * 4]
|
||||
}
|
||||
}
|
||||
|
||||
struct Legend_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
GeometryReader{ geometry in
|
||||
Legend(data: TestData.data, frame: .constant(geometry.frame(in: .local)), hideHorizontalLines: .constant(false))
|
||||
Legend(data: ChartData(points: [0.2,0.4,1.4,4.5]), frame: .constant(geometry.frame(in: .local)), hideHorizontalLines: .constant(false))
|
||||
}.frame(width: 320, height: 200)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,22 @@ struct Line: View {
|
||||
@Binding var showIndicator: Bool
|
||||
@State private var showFull: Bool = false
|
||||
@State var showBackground: Bool = true
|
||||
|
||||
let padding:CGFloat = 30
|
||||
var stepWidth: CGFloat {
|
||||
if data.points.count < 2 {
|
||||
return 0
|
||||
}
|
||||
return frame.size.width / CGFloat(data.points.count-1)
|
||||
}
|
||||
var stepHeight: CGFloat {
|
||||
return frame.size.height / CGFloat(data.points.max()! + data.points.min()!)
|
||||
if let min = data.points.min(), let max = data.points.max(), min != max {
|
||||
if (min <= 0){
|
||||
return (frame.size.height-padding) / CGFloat(data.points.max()! - data.points.min()!)
|
||||
}else{
|
||||
return (frame.size.height-padding) / CGFloat(data.points.max()! + data.points.min()!)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
var path: Path {
|
||||
return Path.quadCurvedPathWithPoints(points: data.points, step: CGPoint(x: stepWidth, y: stepHeight))
|
||||
@@ -67,24 +77,24 @@ struct Line: View {
|
||||
|
||||
extension CGPoint {
|
||||
static func getMidPoint(point1: CGPoint, point2: CGPoint) -> CGPoint {
|
||||
return CGPoint(
|
||||
x: point1.x + (point2.x - point1.x) / 2,
|
||||
y: point1.y + (point2.y - point1.y) / 2
|
||||
)
|
||||
return CGPoint(
|
||||
x: point1.x + (point2.x - point1.x) / 2,
|
||||
y: point1.y + (point2.y - point1.y) / 2
|
||||
)
|
||||
}
|
||||
|
||||
func dist(to: CGPoint) -> CGFloat {
|
||||
return sqrt((pow(self.x - to.x, 2) + pow(self.y - to.y, 2)))
|
||||
return sqrt((pow(self.x - to.x, 2) + pow(self.y - to.y, 2)))
|
||||
}
|
||||
|
||||
static func midPointForPoints(p1:CGPoint, p2:CGPoint) -> CGPoint {
|
||||
return CGPoint(x:(p1.x + p2.x) / 2,y: (p1.y + p2.y) / 2)
|
||||
}
|
||||
|
||||
|
||||
static func controlPointForPoints(p1:CGPoint, p2:CGPoint) -> CGPoint {
|
||||
var controlPoint = CGPoint.midPointForPoints(p1:p1, p2:p2)
|
||||
let diffY = abs(p2.y - controlPoint.y)
|
||||
|
||||
|
||||
if (p1.y < p2.y){
|
||||
controlPoint.y += diffY
|
||||
} else if (p1.y > p2.y) {
|
||||
@@ -94,16 +104,17 @@ extension CGPoint {
|
||||
}
|
||||
}
|
||||
extension Path {
|
||||
static func quadCurvedPathWithPoints(points:[Int], step:CGPoint) -> Path {
|
||||
static func quadCurvedPathWithPoints(points:[Double], step:CGPoint) -> Path {
|
||||
var path = Path()
|
||||
var p1 = CGPoint(x: 0, y: CGFloat(points[0])*step.y)
|
||||
path.move(to: p1)
|
||||
if(points.count < 2){
|
||||
path.addLine(to: CGPoint(x: step.x, y: step.y*CGFloat(points[1])))
|
||||
if (points.count < 2){
|
||||
return path
|
||||
}
|
||||
guard var offset = points.min() else { return path }
|
||||
offset -= 3
|
||||
var p1 = CGPoint(x: 0, y: CGFloat(points[0]-offset)*step.y)
|
||||
path.move(to: p1)
|
||||
for pointIndex in 1..<points.count {
|
||||
let p2 = CGPoint(x: step.x * CGFloat(pointIndex), y: step.y*CGFloat(points[pointIndex]))
|
||||
let p2 = CGPoint(x: step.x * CGFloat(pointIndex), y: step.y*CGFloat(points[pointIndex]-offset))
|
||||
let midPoint = CGPoint.midPointForPoints(p1: p1, p2: p2)
|
||||
path.addQuadCurve(to: midPoint, control: CGPoint.controlPointForPoints(p1: midPoint, p2: p1))
|
||||
path.addQuadCurve(to: p2, control: CGPoint.controlPointForPoints(p1: midPoint, p2: p2))
|
||||
@@ -112,17 +123,18 @@ extension Path {
|
||||
return path
|
||||
}
|
||||
|
||||
static func quadClosedCurvedPathWithPoints(points:[Int], step:CGPoint) -> Path {
|
||||
static func quadClosedCurvedPathWithPoints(points:[Double], step:CGPoint) -> Path {
|
||||
var path = Path()
|
||||
path.move(to: .zero)
|
||||
var p1 = CGPoint(x: 0, y: CGFloat(points[0])*step.y)
|
||||
path.addLine(to: p1)
|
||||
if(points.count < 2){
|
||||
path.addLine(to: CGPoint(x: step.x, y: step.y*CGFloat(points[1])))
|
||||
if (points.count < 2){
|
||||
return path
|
||||
}
|
||||
guard var offset = points.min() else { return path }
|
||||
offset -= 3
|
||||
path.move(to: .zero)
|
||||
var p1 = CGPoint(x: 0, y: CGFloat(points[0]-offset)*step.y)
|
||||
path.addLine(to: p1)
|
||||
for pointIndex in 1..<points.count {
|
||||
let p2 = CGPoint(x: step.x * CGFloat(pointIndex), y: step.y*CGFloat(points[pointIndex]))
|
||||
let p2 = CGPoint(x: step.x * CGFloat(pointIndex), y: step.y*CGFloat(points[pointIndex]-offset))
|
||||
let midPoint = CGPoint.midPointForPoints(p1: p1, p2: p2)
|
||||
path.addQuadCurve(to: midPoint, control: CGPoint.controlPointForPoints(p1: midPoint, p2: p1))
|
||||
path.addQuadCurve(to: p2, control: CGPoint.controlPointForPoints(p1: midPoint, p2: p2))
|
||||
@@ -153,7 +165,7 @@ extension Path {
|
||||
struct Line_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
GeometryReader{ geometry in
|
||||
Line(data: TestData.data, frame: .constant(geometry.frame(in: .local)), touchLocation: .constant(CGPoint(x: 300, y: 12)), showIndicator: .constant(true))
|
||||
Line(data: ChartData(points: [12,-230,10,54]), frame: .constant(geometry.frame(in: .local)), touchLocation: .constant(CGPoint(x: 100, y: 12)), showIndicator: .constant(true))
|
||||
}.frame(width: 320, height: 160)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ public struct LineChartView: View {
|
||||
|
||||
@State private var touchLocation:CGPoint = .zero
|
||||
@State private var showIndicatorDot: Bool = false
|
||||
@State private var currentValue: Int = 2 {
|
||||
@State private var currentValue: Double = 2 {
|
||||
didSet{
|
||||
if (oldValue != self.currentValue && showIndicatorDot) {
|
||||
// selectionFeedbackGenerator.selectionChanged()
|
||||
@@ -31,7 +31,7 @@ public struct LineChartView: View {
|
||||
let frame = CGSize(width: 180, height: 120)
|
||||
private var rateValue: Int
|
||||
|
||||
public init(data: [Int], title: String, legend: String? = nil, style: ChartStyle = Styles.lineChartStyleOne, form: CGSize? = Form.medium ,rateValue: Int? = 14, dropShadow: Bool? = true){
|
||||
public init(data: [Double], title: String, legend: String? = nil, style: ChartStyle = Styles.lineChartStyleOne, form: CGSize? = ChartForm.medium ,rateValue: Int? = 14, dropShadow: Bool? = true){
|
||||
self.data = ChartData(points: data)
|
||||
self.title = title
|
||||
self.legend = legend
|
||||
@@ -66,20 +66,20 @@ public struct LineChartView: View {
|
||||
}else{
|
||||
HStack{
|
||||
Spacer()
|
||||
Text("\(self.currentValue)")
|
||||
Text("\(self.currentValue, specifier: "%.2f")")
|
||||
.font(.system(size: 41, weight: .bold, design: .default))
|
||||
.offset(x: 0, y: 30)
|
||||
Spacer()
|
||||
}
|
||||
.transition(.scale)
|
||||
.animation(.spring())
|
||||
// .animation(.spring())
|
||||
|
||||
}
|
||||
Spacer()
|
||||
GeometryReader{ geometry in
|
||||
Line(data: self.data, frame: .constant(geometry.frame(in: .local)), touchLocation: self.$touchLocation, showIndicator: self.$showIndicatorDot)
|
||||
}
|
||||
.frame(width: frame.width, height: frame.height)
|
||||
.frame(width: frame.width, height: frame.height + 30)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 20))
|
||||
.offset(x: 0, y: 0)
|
||||
}.frame(width: self.formSize.width, height: self.formSize.height)
|
||||
@@ -96,7 +96,7 @@ public struct LineChartView: View {
|
||||
)
|
||||
}
|
||||
|
||||
func getClosestDataPoint(toPoint: CGPoint, width:CGFloat, height: CGFloat) -> CGPoint {
|
||||
@discardableResult func getClosestDataPoint(toPoint: CGPoint, width:CGFloat, height: CGFloat) -> CGPoint {
|
||||
let stepWidth: CGFloat = width / CGFloat(data.points.count-1)
|
||||
let stepHeight: CGFloat = height / CGFloat(data.points.max()! + data.points.min()!)
|
||||
|
||||
|
||||
@@ -20,17 +20,17 @@ public struct LineView: View {
|
||||
@State private var indicatorLocation:CGPoint = .zero
|
||||
@State private var closestPoint: CGPoint = .zero
|
||||
@State private var opacity:Double = 0
|
||||
@State private var currentDataNumber: Int = 0
|
||||
@State private var currentDataNumber: Double = 0
|
||||
@State private var hideHorizontalLines: Bool = false
|
||||
|
||||
public init(data: [Int], title: String? = nil, legend: String? = nil, style: ChartStyle? = Styles.lineChartStyleOne){
|
||||
public init(data: [Double], title: String? = nil, legend: String? = nil, style: ChartStyle? = Styles.lineChartStyleOne){
|
||||
self.data = ChartData(points: data)
|
||||
self.title = title
|
||||
self.legend = legend
|
||||
self.style = style!
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
public var body: some View {
|
||||
GeometryReader{ geometry in
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Group{
|
||||
@@ -40,7 +40,7 @@ public struct LineView: View {
|
||||
if (self.legend != nil){
|
||||
Text(self.legend!).font(.callout).foregroundColor(self.colorScheme == .dark ? Color.white : self.style.legendTextColor)
|
||||
}
|
||||
}.offset(x: 0, y: 46)
|
||||
}.offset(x: 0, y: 20)
|
||||
ZStack{
|
||||
GeometryReader{ reader in
|
||||
Rectangle().foregroundColor(self.colorScheme == .dark ? Color.black : self.style.backgroundColor)
|
||||
@@ -56,7 +56,7 @@ public struct LineView: View {
|
||||
}.frame(width: geometry.frame(in: .local).size.width, height: 240).offset(x: 0, y: 40 )
|
||||
MagnifierRect(currentNumber: self.$currentDataNumber)
|
||||
.opacity(self.opacity)
|
||||
.offset(x: self.dragLocation.x - geometry.frame(in: .local).size.width/2, y: 50)
|
||||
.offset(x: self.dragLocation.x - geometry.frame(in: .local).size.width/2, y: 36)
|
||||
}
|
||||
.frame(width: geometry.frame(in: .local).size.width, height: 240)
|
||||
.gesture(DragGesture()
|
||||
@@ -91,7 +91,7 @@ public struct LineView: View {
|
||||
|
||||
struct LineView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
LineView(data: [37,72,51,22,39,47,66,85,50], title: "Full chart", style: Styles.lineChartStyleOne).environment(\.colorScheme, .dark)
|
||||
LineView(data: [8,23,54,32,12,37,7,23,43], title: "Full chart", style: Styles.lineChartStyleOne)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,21 +104,28 @@ struct IndicatorCircle: View {
|
||||
}
|
||||
|
||||
struct MagnifierRect: View {
|
||||
@Binding var currentNumber:Int
|
||||
@Binding var currentNumber: Double
|
||||
@Environment(\.colorScheme) var colorScheme: ColorScheme
|
||||
var body: some View {
|
||||
ZStack{
|
||||
Text("\(self.currentNumber)")
|
||||
Text("\(self.currentNumber, specifier: "%.2f")")
|
||||
.font(.system(size: 18, weight: .bold))
|
||||
.offset(x: 0, y:-96)
|
||||
.offset(x: 0, y:-110)
|
||||
.animation(.spring())
|
||||
.foregroundColor(self.colorScheme == .dark ? Color.white : Color.black)
|
||||
RoundedRectangle(cornerRadius: 16)
|
||||
.stroke(Color.white, lineWidth: self.colorScheme == .dark ? 2 : 0)
|
||||
.frame(width: 60, height: 240)
|
||||
.foregroundColor(self.colorScheme == .dark ? Color.black : Color.white)
|
||||
.shadow(color: Colors.LegendText, radius: 12, x: 0, y: 6 )
|
||||
.blendMode(self.colorScheme == .dark ? .normal : .multiply)
|
||||
if (self.colorScheme == .dark ){
|
||||
RoundedRectangle(cornerRadius: 16)
|
||||
.stroke(Color.white, lineWidth: self.colorScheme == .dark ? 2 : 0)
|
||||
.frame(width: 60, height: 260)
|
||||
|
||||
}else{
|
||||
RoundedRectangle(cornerRadius: 16)
|
||||
.frame(width: 60, height: 280)
|
||||
.foregroundColor(Color.white)
|
||||
.shadow(color: Colors.LegendText, radius: 12, x: 0, y: 6 )
|
||||
.blendMode(.multiply)
|
||||
}
|
||||
|
||||
|
||||
}.animation(.linear)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ struct PieSlice: Identifiable {
|
||||
var id = UUID()
|
||||
var startDeg: Double
|
||||
var endDeg: Double
|
||||
var value: Int
|
||||
var value: Double
|
||||
var normalizedValue: Double
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct PieChartRow : View {
|
||||
var data: [Int]
|
||||
var data: [Double]
|
||||
var backgroundColor: Color
|
||||
var accentColor: Color
|
||||
var slices: [PieSlice] {
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct PieChartView : View {
|
||||
public var data: [Int]
|
||||
public var data: [Double]
|
||||
public var title: String
|
||||
public var legend: String?
|
||||
public var style: ChartStyle
|
||||
public var formSize:CGSize
|
||||
public var dropShadow: Bool
|
||||
|
||||
public init(data: [Int], title: String, legend: String? = nil, style: ChartStyle = Styles.pieChartStyleOne, form: CGSize? = Form.medium, dropShadow: Bool? = true ){
|
||||
public init(data: [Double], title: String, legend: String? = nil, style: ChartStyle = Styles.pieChartStyleOne, form: CGSize? = ChartForm.medium, dropShadow: Bool? = true ){
|
||||
self.data = data
|
||||
self.title = title
|
||||
self.legend = legend
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 514 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 502 KiB |
Reference in New Issue
Block a user