Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b5e3aa897c | |||
| d3d0b086f1 | |||
| 6cc43d9dc0 | |||
| e081d3a88d | |||
| e5c309eac4 | |||
| 3f542f92e3 | |||
| 7a9f013631 | |||
| 8dffe79e28 | |||
| d5a1c0065c | |||
| b9761d3eb9 | |||
| c3c0a8a7c4 | |||
| b597faac76 | |||
| fc1099f486 |
Generated
BIN
Binary file not shown.
@@ -21,7 +21,22 @@ import the package in the file you would like to use it: `import SwiftUICharts`
|
||||
|
||||
You can display a Chart by adding a chart view to your parent view:
|
||||
|
||||
### Demo
|
||||
|
||||
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**
|
||||
@@ -32,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
|
||||

|
||||
@@ -50,9 +67,10 @@ You can add different formats:
|
||||
* Large `Form.large`
|
||||
|
||||
```swift
|
||||
BarChartView(data: [8,23,54,32,12,37,7,23,43], title: "Title", style: ChartStyle(formSize: Form.small))
|
||||
BarChartView(data: [8,23,54,32,12,37,7,23,43], title: "Title", form: Form.small)
|
||||
```
|
||||
|
||||
**Turn drop shadow off by adding to the Initialiser: `dropShadow: false`**
|
||||
|
||||
### You can customize styling of the chart with a ChartStyle object:
|
||||
|
||||
Customizable:
|
||||
@@ -110,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`**
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ public struct BarChartView : View {
|
||||
public var legend: String?
|
||||
public var style: ChartStyle
|
||||
public var formSize:CGSize
|
||||
public var dropShadow: Bool
|
||||
|
||||
// let selectionFeedbackGenerator = UISelectionFeedbackGenerator()
|
||||
|
||||
@State private var touchLocation: CGFloat = -1.0
|
||||
@@ -29,12 +31,13 @@ public struct BarChartView : View {
|
||||
var isFullWidth:Bool {
|
||||
return self.formSize == Form.large
|
||||
}
|
||||
public init(data: [Int], title: String, legend: String? = nil, style: ChartStyle = Styles.barChartStyleOrangeLight, form: CGSize? = Form.medium){
|
||||
public init(data: [Int], title: String, legend: String? = nil, style: ChartStyle = Styles.barChartStyleOrangeLight, form: CGSize? = Form.medium, dropShadow: Bool? = true){
|
||||
self.data = data
|
||||
self.title = title
|
||||
self.legend = legend
|
||||
self.style = style
|
||||
self.formSize = form!
|
||||
self.dropShadow = dropShadow!
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
@@ -42,7 +45,7 @@ public struct BarChartView : View {
|
||||
Rectangle()
|
||||
.fill(self.style.backgroundColor)
|
||||
.cornerRadius(20)
|
||||
.shadow(color: Color.gray, radius: 8 )
|
||||
.shadow(color: Color.gray, radius: self.dropShadow ? 8 : 0)
|
||||
VStack(alignment: .leading){
|
||||
HStack{
|
||||
if(!showValue){
|
||||
@@ -93,7 +96,6 @@ public struct BarChartView : View {
|
||||
|
||||
func getCurrentValue()-> Int{
|
||||
let index = max(0,min(self.data.count-1,Int(floor((self.touchLocation*self.formSize.width)/(self.formSize.width/CGFloat(self.data.count))))))
|
||||
print(index)
|
||||
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")
|
||||
@@ -143,9 +144,9 @@ class TestData{
|
||||
extension Color {
|
||||
init(hexString: String) {
|
||||
let hex = hexString.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
|
||||
var int = UInt32()
|
||||
Scanner(string: hex).scanHexInt32(&int)
|
||||
let r, g, b: UInt32
|
||||
var int = UInt64()
|
||||
Scanner(string: hex).scanHexInt64(&int)
|
||||
let r, g, b: UInt64
|
||||
switch hex.count {
|
||||
case 3: // RGB (12-bit)
|
||||
(r, g, b) = ((int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
|
||||
|
||||
@@ -12,7 +12,8 @@ struct Legend: View {
|
||||
@ObservedObject var data: ChartData
|
||||
@Binding var frame: CGRect
|
||||
@Binding var hideHorizontalLines: Bool
|
||||
|
||||
@Environment(\.colorScheme) var colorScheme: ColorScheme
|
||||
|
||||
var stepWidth: CGFloat {
|
||||
return frame.size.width / CGFloat(data.points.count-1)
|
||||
}
|
||||
@@ -28,7 +29,7 @@ struct Legend: View {
|
||||
.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]))
|
||||
.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))
|
||||
|
||||
@@ -15,6 +15,8 @@ public struct LineChartView: View {
|
||||
public var legend: String?
|
||||
public var style: ChartStyle
|
||||
public var formSize:CGSize
|
||||
public var dropShadow: Bool
|
||||
|
||||
@State private var touchLocation:CGPoint = .zero
|
||||
@State private var showIndicatorDot: Bool = false
|
||||
@State private var currentValue: Int = 2 {
|
||||
@@ -27,18 +29,21 @@ 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){
|
||||
public init(data: [Int], title: String, legend: String? = nil, style: ChartStyle = Styles.lineChartStyleOne, form: CGSize? = Form.medium ,rateValue: Int? = 14, dropShadow: Bool? = true){
|
||||
self.data = ChartData(points: data)
|
||||
self.title = title
|
||||
self.legend = legend
|
||||
self.style = style
|
||||
self.formSize = form!
|
||||
self.rateValue = rateValue!
|
||||
self.dropShadow = dropShadow!
|
||||
}
|
||||
|
||||
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: 8)
|
||||
RoundedRectangle(cornerRadius: 20).fill(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){
|
||||
@@ -47,8 +52,12 @@ public struct LineChartView: View {
|
||||
Text(self.legend!).font(.callout).foregroundColor(self.style.legendTextColor)
|
||||
}
|
||||
HStack {
|
||||
Image(systemName: "arrow.up")
|
||||
Text("14%")
|
||||
if (self.rateValue >= 0){
|
||||
Image(systemName: "arrow.up")
|
||||
}else{
|
||||
Image(systemName: "arrow.down")
|
||||
}
|
||||
Text("\(self.rateValue)%")
|
||||
}
|
||||
}
|
||||
.transition(.opacity)
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
//
|
||||
// LineView.swift
|
||||
// LineChart
|
||||
//
|
||||
// Created by András Samu on 2019. 09. 02..
|
||||
// Copyright © 2019. András Samu. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
public struct LineView: View {
|
||||
@ObservedObject var data: ChartData
|
||||
public var title: String?
|
||||
public var legend: String?
|
||||
public var style: ChartStyle
|
||||
|
||||
@Environment(\.colorScheme) var colorScheme: ColorScheme
|
||||
@State private var showLegend = false
|
||||
@State private var dragLocation:CGPoint = .zero
|
||||
@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 hideHorizontalLines: Bool = false
|
||||
|
||||
public init(data: [Int], 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 {
|
||||
GeometryReader{ geometry in
|
||||
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)
|
||||
}
|
||||
if (self.legend != nil){
|
||||
Text(self.legend!).font(.callout).foregroundColor(self.colorScheme == .dark ? Color.white : self.style.legendTextColor)
|
||||
}
|
||||
}.offset(x: 0, y: 20)
|
||||
ZStack{
|
||||
GeometryReader{ reader in
|
||||
Rectangle().foregroundColor(self.colorScheme == .dark ? Color.black : self.style.backgroundColor)
|
||||
if(self.showLegend){
|
||||
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)
|
||||
.onAppear(){
|
||||
self.showLegend.toggle()
|
||||
}
|
||||
}.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: 36)
|
||||
}
|
||||
.frame(width: geometry.frame(in: .local).size.width, height: 240)
|
||||
.gesture(DragGesture()
|
||||
.onChanged({ value in
|
||||
self.dragLocation = value.location
|
||||
self.indicatorLocation = CGPoint(x: max(value.location.x-30,0), y: 32)
|
||||
self.opacity = 1
|
||||
self.closestPoint = self.getClosestDataPoint(toPoint: value.location, width: geometry.frame(in: .local).size.width-30, height: 240)
|
||||
self.hideHorizontalLines = true
|
||||
})
|
||||
.onEnded({ value in
|
||||
self.opacity = 0
|
||||
self.hideHorizontalLines = false
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()!)
|
||||
|
||||
let index:Int = Int(floor((toPoint.x-15)/stepWidth))
|
||||
if (index >= 0 && index < data.points.count){
|
||||
self.currentDataNumber = self.data.points[index]
|
||||
return CGPoint(x: CGFloat(index)*stepWidth, y: CGFloat(self.data.points[index])*stepHeight)
|
||||
}
|
||||
return .zero
|
||||
}
|
||||
}
|
||||
|
||||
struct LineView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
LineView(data: [8,23,54,32,12,37,7,23,43], title: "Full chart", style: Styles.lineChartStyleOne)
|
||||
}
|
||||
}
|
||||
|
||||
struct IndicatorCircle: View {
|
||||
var body: some View {
|
||||
Circle()
|
||||
.size(width: 12, height: 12)
|
||||
.fill(Colors.BorderBlue)
|
||||
}
|
||||
}
|
||||
|
||||
struct MagnifierRect: View {
|
||||
@Binding var currentNumber:Int
|
||||
@Environment(\.colorScheme) var colorScheme: ColorScheme
|
||||
var body: some View {
|
||||
ZStack{
|
||||
Text("\(self.currentNumber)")
|
||||
.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)
|
||||
.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)
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,15 @@ public struct PieChartView : View {
|
||||
public var legend: String?
|
||||
public var style: ChartStyle
|
||||
public var formSize:CGSize
|
||||
public init(data: [Int], title: String, legend: String? = nil, style: ChartStyle = Styles.pieChartStyleOne, form: CGSize? = Form.medium ){
|
||||
public var dropShadow: Bool
|
||||
|
||||
public init(data: [Int], title: String, legend: String? = nil, style: ChartStyle = Styles.pieChartStyleOne, form: CGSize? = Form.medium, dropShadow: Bool? = true ){
|
||||
self.data = data
|
||||
self.title = title
|
||||
self.legend = legend
|
||||
self.style = style
|
||||
self.formSize = form!
|
||||
self.dropShadow = dropShadow!
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
@@ -27,7 +30,7 @@ public struct PieChartView : View {
|
||||
Rectangle()
|
||||
.fill(self.style.backgroundColor)
|
||||
.cornerRadius(20)
|
||||
.shadow(color: Color.gray, radius: 12)
|
||||
.shadow(color: Color.gray, radius: self.dropShadow ? 12 : 0)
|
||||
VStack(alignment: .leading){
|
||||
HStack{
|
||||
Text(self.title)
|
||||
|
||||
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