Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 102b51bff6 | |||
| 13ab3af479 | |||
| c360f8a685 | |||
| 6acb264fb2 | |||
| a4381e037c | |||
| aecfdc5634 | |||
| 9115a992c9 | |||
| eca6eda179 | |||
| 1f4949a731 | |||
| 92490983c5 | |||
| 5c49a55e68 | |||
| 29700ffd47 | |||
| 5aa8cdf50b | |||
| 08f0053b16 |
@@ -2,3 +2,4 @@
|
||||
/.build
|
||||
/Packages
|
||||
/*.xcodeproj
|
||||
.swiftpm
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// swift-tools-version:5.1
|
||||
// swift-tools-version:5.3
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
@@ -6,7 +6,7 @@ import PackageDescription
|
||||
let package = Package(
|
||||
name: "SwiftUICharts",
|
||||
platforms: [
|
||||
.iOS(.v13), .watchOS(.v6), .macOS(.v10_15)
|
||||
.iOS(.v13), .watchOS(.v6), .macOS(.v11)
|
||||
],
|
||||
products: [
|
||||
// Products define the executables and libraries produced by a package, and make them visible to other packages.
|
||||
|
||||
@@ -2,15 +2,27 @@
|
||||
|
||||
Swift package for displaying charts effortlessly.
|
||||
|
||||
**V2 Beta 1 is released, if you would like to try it out you can find a [demo project here](https://github.com/AppPear/ChartViewV2Demo)**
|
||||
## V2 Beta is here 🎉🎉🎉
|
||||
|
||||
**iOS 14 WidgetKit support is coming. In V2 I will update current charts and possibly extend with some new chart types to provide the best support for building informative and beautiful widgets for the new home screen 🥳 Stay tuned!**
|
||||
V2 focuses on providing a strong and easy to use base, on which you can build your beautiful custom charts. It provides basic building blocks, like a chart view (bar, pie, line and ring chart), grid view, card view, interactive label for displaying the curent chart value.
|
||||
So you decide, whether you build a fully fledged interactive view, or just display a bare bone chart
|
||||
|
||||

|
||||
* [How to install SwiftUI ChartView](https://github.com/AppPear/ChartView/wiki/How-to-install-SwiftUI-ChartView)
|
||||
|
||||
### Note:
|
||||
**A version 2.0 is coming soon!!! 🎉🎉🎉**, so please hold off your PRs for a while. I'm writing a new code base with more sleek code architecture with an option for easier expansion. I'll make beta releases so you can test betas.
|
||||
**If you'd like to contribute you can find tickets for the new version in the Issues under the `v2` tag, please read more at: [https://github.com/AppPear/ChartView/pull/89](https://github.com/AppPear/ChartView/pull/89)**
|
||||
* [How to create your first chart](https://github.com/AppPear/ChartView/wiki/How-to-create-your-first-chart)
|
||||
|
||||
### It supports interactions and animations
|
||||
<img src="https://user-images.githubusercontent.com/2826764/130787802-9aa619ee-05de-4343-ba3c-1796e4d05e08.gif" width="26%"></img> <img src="https://user-images.githubusercontent.com/2826764/130787814-283f3d26-6c9d-448b-b2c7-879e60a3b05d.gif" width="26%"></img>
|
||||
|
||||
### It is fully customizable, and works together with native SwiftUI elements well
|
||||
<img src="https://user-images.githubusercontent.com/2826764/130785262-010d6791-16cf-485d-b920-29e4086477e2.png" width="45%"></img> <img src="https://user-images.githubusercontent.com/2826764/130785266-94a08622-2963-4177-8777-8bd3ad463809.png" width="45%"></img> <img src="https://user-images.githubusercontent.com/2826764/130785268-284314de-ba96-4fb7-a1e5-8a46578e1f0e.png" width="45%"></img>
|
||||
|
||||
|
||||
## Original (stable) version:
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/2826764/131211993-5d33312b-09af-44b4-a32e-ffaad739adfe.gif" width="45%"></img> <img src="https://user-images.githubusercontent.com/2826764/131211994-48c9ce4e-2e67-40a0-b727-c88bdbd22cd0.gif" width="45%">
|
||||
|
||||
### Usage
|
||||
|
||||
It supports:
|
||||
* Line charts
|
||||
@@ -20,7 +32,7 @@ It supports:
|
||||
### Slack
|
||||
Join our Slack channel for day to day conversation and more insights:
|
||||
|
||||
[Slack invite link](https://join.slack.com/t/swiftuichartview/shared_invite/zt-el5pnmba-FvyraEsI~EwxqWHNfHZWZg)
|
||||
[Slack invite link](https://join.slack.com/t/swiftuichartview/shared_invite/zt-g6mxioq8-j3iUTF1YKX7D23ML3qcc4g)
|
||||
|
||||
### Installation:
|
||||
|
||||
@@ -42,7 +54,8 @@ Added an example project, with **iOS, watchOS** target: https://github.com/AppPe
|
||||
|
||||
**LineChartView with multiple lines!**
|
||||
First release of this feature, interaction is disabled for now, I'll figure it out how could be the best to interact with multiple lines with a single touch.
|
||||

|
||||
|
||||
<img src="https://user-images.githubusercontent.com/2826764/131211991-eca64276-cf05-423f-a78a-697c55e44bbc.gif" width="50%"></img>
|
||||
|
||||
Usage:
|
||||
```swift
|
||||
@@ -63,7 +76,6 @@ Available preset gradients:
|
||||
|
||||
**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()
|
||||
@@ -71,7 +83,7 @@ Available preset gradients:
|
||||
|
||||
Adopts to dark mode automatically
|
||||
|
||||

|
||||
<img src="https://user-images.githubusercontent.com/2826764/131211977-27439357-491d-4872-a6bd-f696edac4c7f.gif" width="45%"></img> <img src="https://user-images.githubusercontent.com/2826764/131211985-f77464d6-7fd8-429d-9e77-9f9bc7424d32.gif" width="45%">
|
||||
|
||||
You can add your custom darkmode style by specifying:
|
||||
|
||||
@@ -93,7 +105,7 @@ You can add a line chart with the following code:
|
||||
|
||||
|
||||
## Bar charts
|
||||

|
||||
<img src="https://user-images.githubusercontent.com/2826764/131211994-48c9ce4e-2e67-40a0-b727-c88bdbd22cd0.gif" width="45%">
|
||||
|
||||
**[New feature] you can display labels also along values and points for each bar to descirbe your data better!**
|
||||
**Bar chart is interactive, so you can drag across to reveal the data points**
|
||||
@@ -171,10 +183,9 @@ You can access built-in styles:
|
||||
* barChartMidnightGreenLight
|
||||
* barChartMidnightGreenDark
|
||||
|
||||

|
||||
|
||||

|
||||
<img src="https://user-images.githubusercontent.com/2826764/131211990-e41cec90-38f4-4965-8bdc-41c30b79acea.gif" width="45%">
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/2826764/131211999-6ec4f13b-0465-4135-b576-76e31b11a2c6.png" width="45%">
|
||||
|
||||
### You can customize the size of the chart with a ChartForm object:
|
||||
|
||||
@@ -188,12 +199,16 @@ You can access built-in styles:
|
||||
BarChartView(data: [8,23,54,32,12,37,7,23,43], title: "Title", form: ChartForm.small)
|
||||
```
|
||||
|
||||
### You can choose whether bar is animated or not after completing your gesture.
|
||||
|
||||
If you want to animate back movement after completing your gesture, you set `animatedToBack` as `true`.
|
||||
|
||||
### WatchOS support for Bar charts:
|
||||
|
||||

|
||||
<img src="https://user-images.githubusercontent.com/2826764/131212000-a058fdd9-af40-4e64-adc3-82201ea2484d.png" width="45%">
|
||||
|
||||
## Pie charts
|
||||

|
||||
<img src="https://user-images.githubusercontent.com/2826764/131211998-e142657d-0ebc-43b7-aeda-07cae4d9e34b.png" width="45%">
|
||||
|
||||
You can add a pie chart with the following code:
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 514 KiB |
|
Before Width: | Height: | Size: 502 KiB |
|
Before Width: | Height: | Size: 222 KiB |
|
Before Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 147 KiB |
|
Before Width: | Height: | Size: 343 KiB |
|
Before Width: | Height: | Size: 278 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 73 KiB |
@@ -17,8 +17,9 @@ public struct BarChartView : View {
|
||||
public var darkModeStyle: ChartStyle
|
||||
public var formSize:CGSize
|
||||
public var dropShadow: Bool
|
||||
public var cornerImage: Image
|
||||
public var cornerImage: Image?
|
||||
public var valueSpecifier:String
|
||||
public var animatedToBack: Bool
|
||||
|
||||
@State private var touchLocation: CGFloat = -1.0
|
||||
@State private var showValue: Bool = false
|
||||
@@ -33,7 +34,7 @@ public struct BarChartView : View {
|
||||
var isFullWidth:Bool {
|
||||
return self.formSize == ChartForm.large
|
||||
}
|
||||
public init(data:ChartData, title: String, legend: String? = nil, style: ChartStyle = Styles.barChartStyleOrangeLight, form: CGSize? = ChartForm.medium, dropShadow: Bool? = true, cornerImage:Image? = Image(systemName: "waveform.path.ecg"), valueSpecifier: String? = "%.1f"){
|
||||
public init(data:ChartData, title: String, legend: String? = nil, style: ChartStyle = Styles.barChartStyleOrangeLight, form: CGSize? = ChartForm.medium, dropShadow: Bool? = true, cornerImage:Image? = Image(systemName: "waveform.path.ecg"), valueSpecifier: String? = "%.1f", animatedToBack: Bool = false){
|
||||
self.data = data
|
||||
self.title = title
|
||||
self.legend = legend
|
||||
@@ -41,8 +42,9 @@ public struct BarChartView : View {
|
||||
self.darkModeStyle = style.darkModeStyle != nil ? style.darkModeStyle! : Styles.barChartStyleOrangeDark
|
||||
self.formSize = form!
|
||||
self.dropShadow = dropShadow!
|
||||
self.cornerImage = cornerImage!
|
||||
self.cornerImage = cornerImage
|
||||
self.valueSpecifier = valueSpecifier!
|
||||
self.animatedToBack = animatedToBack
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
@@ -105,9 +107,19 @@ public struct BarChartView : View {
|
||||
}
|
||||
})
|
||||
.onEnded({ value in
|
||||
self.showValue = false
|
||||
self.showLabelValue = false
|
||||
self.touchLocation = -1
|
||||
if animatedToBack {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
withAnimation(Animation.easeOut(duration: 1)) {
|
||||
self.showValue = false
|
||||
self.showLabelValue = false
|
||||
self.touchLocation = -1
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.showValue = false
|
||||
self.showLabelValue = false
|
||||
self.touchLocation = -1
|
||||
}
|
||||
})
|
||||
)
|
||||
.gesture(TapGesture()
|
||||
|
||||
@@ -268,11 +268,15 @@ class HapticFeedback {
|
||||
static func playSelection() -> Void {
|
||||
WKInterfaceDevice.current().play(.click)
|
||||
}
|
||||
#else
|
||||
#elseif os(iOS)
|
||||
//iOS implementation
|
||||
let selectionFeedbackGenerator = UISelectionFeedbackGenerator()
|
||||
static func playSelection() -> Void {
|
||||
UISelectionFeedbackGenerator().selectionChanged()
|
||||
}
|
||||
#else
|
||||
static func playSelection() -> Void {
|
||||
//No-op
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ struct Legend: View {
|
||||
@Binding var frame: CGRect
|
||||
@Binding var hideHorizontalLines: Bool
|
||||
@Environment(\.colorScheme) var colorScheme: ColorScheme
|
||||
var specifier: String = "%.2f"
|
||||
let padding:CGFloat = 3
|
||||
|
||||
var stepWidth: CGFloat {
|
||||
@@ -42,7 +43,7 @@ struct Legend: View {
|
||||
ZStack(alignment: .topLeading){
|
||||
ForEach((0...4), id: \.self) { height in
|
||||
HStack(alignment: .center){
|
||||
Text("\(self.getYLegendSafe(height: height), specifier: "%.2f")").offset(x: 0, y: self.getYposition(height: height) )
|
||||
Text("\(self.getYLegendSafe(height: height), specifier: specifier)").offset(x: 0, y: self.getYposition(height: height) )
|
||||
.foregroundColor(Colors.LegendText)
|
||||
.font(.caption)
|
||||
self.line(atHeight: self.getYLegendSafe(height: height), width: self.frame.width)
|
||||
|
||||
@@ -80,7 +80,6 @@ public struct Line: View {
|
||||
.onDisappear {
|
||||
self.showFull = false
|
||||
}
|
||||
.drawingGroup()
|
||||
if(self.showIndicator) {
|
||||
IndicatorPoint()
|
||||
.position(self.getClosestPointOnPath(touchLocation: self.touchLocation))
|
||||
|
||||
@@ -38,7 +38,7 @@ public struct LineChartView: View {
|
||||
legend: String? = nil,
|
||||
style: ChartStyle = Styles.lineChartStyleOne,
|
||||
form: CGSize? = ChartForm.medium,
|
||||
rateValue: Int? = 14,
|
||||
rateValue: Int?,
|
||||
dropShadow: Bool? = true,
|
||||
valueSpecifier: String? = "%.1f") {
|
||||
|
||||
@@ -74,14 +74,14 @@ public struct LineChartView: View {
|
||||
}
|
||||
HStack {
|
||||
|
||||
if (self.rateValue ?? 0 != 0)
|
||||
if let rateValue = self.rateValue
|
||||
{
|
||||
if (self.rateValue ?? 0 >= 0){
|
||||
if (rateValue ?? 0 >= 0){
|
||||
Image(systemName: "arrow.up")
|
||||
}else{
|
||||
Image(systemName: "arrow.down")
|
||||
}
|
||||
Text("\(self.rateValue!)%")
|
||||
Text("\(rateValue!)%")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ public struct LineChartView: View {
|
||||
maxDataValue: .constant(nil)
|
||||
)
|
||||
}
|
||||
.frame(width: frame.width, height: frame.height + 30)
|
||||
.frame(width: frame.width, height: frame.height)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 20))
|
||||
.offset(x: 0, y: 0)
|
||||
}.frame(width: self.formSize.width, height: self.formSize.height)
|
||||
|
||||
@@ -14,7 +14,8 @@ public struct LineView: View {
|
||||
public var legend: String?
|
||||
public var style: ChartStyle
|
||||
public var darkModeStyle: ChartStyle
|
||||
public var valueSpecifier:String
|
||||
public var valueSpecifier: String
|
||||
public var legendSpecifier: String
|
||||
|
||||
@Environment(\.colorScheme) var colorScheme: ColorScheme
|
||||
@State private var showLegend = false
|
||||
@@ -29,13 +30,15 @@ public struct LineView: View {
|
||||
title: String? = nil,
|
||||
legend: String? = nil,
|
||||
style: ChartStyle = Styles.lineChartStyleOne,
|
||||
valueSpecifier: String? = "%.1f") {
|
||||
valueSpecifier: String? = "%.1f",
|
||||
legendSpecifier: String? = "%.2f") {
|
||||
|
||||
self.data = ChartData(points: data)
|
||||
self.title = title
|
||||
self.legend = legend
|
||||
self.style = style
|
||||
self.valueSpecifier = valueSpecifier!
|
||||
self.legendSpecifier = legendSpecifier!
|
||||
self.darkModeStyle = style.darkModeStyle != nil ? style.darkModeStyle! : Styles.lineViewDarkMode
|
||||
}
|
||||
|
||||
@@ -60,12 +63,12 @@ public struct LineView: View {
|
||||
.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)
|
||||
frame: .constant(reader.frame(in: .local)), hideHorizontalLines: self.$hideHorizontalLines, specifier: legendSpecifier)
|
||||
.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)),
|
||||
frame: .constant(CGRect(x: 0, y: 0, width: reader.frame(in: .local).width - 30, height: reader.frame(in: .local).height + 25)),
|
||||
touchLocation: self.$indicatorLocation,
|
||||
showIndicator: self.$hideHorizontalLines,
|
||||
minDataValue: .constant(nil),
|
||||
@@ -73,7 +76,7 @@ public struct LineView: View {
|
||||
showBackground: false,
|
||||
gradient: self.style.gradientColor
|
||||
)
|
||||
.offset(x: 30, y: -20)
|
||||
.offset(x: 30, y: 0)
|
||||
.onAppear(){
|
||||
self.showLegend = true
|
||||
}
|
||||
|
||||
@@ -29,5 +29,6 @@ public struct MagnifierRect: View {
|
||||
.blendMode(.multiply)
|
||||
}
|
||||
}
|
||||
.offset(x: 0, y: -15)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ public struct MultiLineChartView: View {
|
||||
Spacer()
|
||||
GeometryReader{ geometry in
|
||||
ZStack{
|
||||
ForEach(0..<self.data.count) { i in
|
||||
ForEach(0..<self.data.count, id: \.self) { i in
|
||||
Line(data: self.data[i],
|
||||
frame: .constant(geometry.frame(in: .local)),
|
||||
touchLocation: self.$touchLocation,
|
||||
|
||||