Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9115a992c9 | |||
| eca6eda179 | |||
| 1f4949a731 | |||
| 92490983c5 | |||
| 5c49a55e68 | |||
| 29700ffd47 | |||
| 5aa8cdf50b | |||
| 08f0053b16 | |||
| 4699847a9a | |||
| 6c768ad47e | |||
| 6c612fae18 | |||
| 812e02815f | |||
| c6610f5679 | |||
| 47052674f5 | |||
| a36c1db2f8 | |||
| ca19dd578c | |||
| 27e7e0dd1d | |||
| 74140af7a7 | |||
| 7568c5d40c | |||
| 24cf9eacb8 | |||
| c89b1e1480 | |||
| 82e8f249cc | |||
| 22a38a1d40 | |||
| b5b7c62645 | |||
| 3447d5c9bb | |||
| 640ddeb4d2 | |||
| ebd09f438e | |||
| 0303c3c14d | |||
| 068ea84ddf | |||
| fa8e015794 | |||
| dd7a1fc9bd | |||
| fada162030 | |||
| a242bd3c94 | |||
| c12c773af0 | |||
| 75804f470a |
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
|
||||
|
||||
## Description
|
||||
<!--- Provide a more detailed introduction to the issue itself, and why you consider it to be a bug -->
|
||||
|
||||
## Expected Behavior
|
||||
<!--- Tell us what should happen -->
|
||||
|
||||
## Actual Behavior
|
||||
<!--- Tell us what happens instead -->
|
||||
|
||||
## Possible Fix
|
||||
<!--- Not obligatory, but suggest a fix or reason for the bug -->
|
||||
|
||||
## Steps to Reproduce
|
||||
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
|
||||
<!--- reproduce this bug. Include code to reproduce, if relevant -->
|
||||
|
||||
## Your Environment
|
||||
<!--- Include as many relevant details about the environment -->
|
||||
* Version of this package used:
|
||||
* Device/Simulator:
|
||||
* Operating System and version:
|
||||
* Link to your project:
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Ask for a new feature
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
## Detailed Description
|
||||
<!--- Provide a detailed description of the change or addition you are proposing -->
|
||||
|
||||
## Context
|
||||
<!--- Why is this change important to you? How would you use it? -->
|
||||
<!--- How can it benefit other users? -->
|
||||
|
||||
## Possible Implementation
|
||||
<!--- Not obligatory, but suggest an idea for implementing addition or change -->
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
name: v2 ticket
|
||||
about: Create tasks for the upcoming new version
|
||||
title: ''
|
||||
labels: v2
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
# v2 ticket
|
||||
|
||||
## Ticket description:
|
||||
@@ -0,0 +1,29 @@
|
||||
<!--- Provide a general summary of your changes in the Title above -->
|
||||
|
||||
## Description
|
||||
<!--- Describe your changes in detail -->
|
||||
|
||||
## Motivation and Context
|
||||
<!--- Why is this change required? What problem does it solve? -->
|
||||
<!--- If it fixes an open issue, please link to the issue here. -->
|
||||
|
||||
## How Has This Been Tested?
|
||||
<!--- Please describe in detail how you tested your changes. -->
|
||||
<!--- Include details of your testing environment, and the tests you ran to -->
|
||||
<!--- see how your change affects other areas of the code, etc. -->
|
||||
|
||||
## Screenshots (if appropriate):
|
||||
|
||||
## Types of changes
|
||||
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
|
||||
- [ ] Non-functional change (Updating Documentation, CI automation, etc..)
|
||||
|
||||
## Checklist:
|
||||
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
|
||||
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||
- [ ] My code follows the code style of this project.
|
||||
- [ ] My change requires a change to the documentation.
|
||||
- [ ] I have updated the documentation accordingly.
|
||||
@@ -0,0 +1,22 @@
|
||||
name: Swift
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- new-version
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- new-version
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build
|
||||
run: swift build -v
|
||||
- name: Run tests
|
||||
run: swift test -v
|
||||
@@ -2,3 +2,4 @@
|
||||
/.build
|
||||
/Packages
|
||||
/*.xcodeproj
|
||||
.swiftpm
|
||||
|
||||
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
@@ -7,7 +7,7 @@
|
||||
<key>SwiftUICharts.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
|
||||
+2
-2
@@ -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)
|
||||
.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,18 +2,31 @@
|
||||
|
||||
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)**
|
||||
|
||||
**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!**
|
||||
|
||||

|
||||
|
||||
### 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)**
|
||||
|
||||
It supports:
|
||||
* Line charts
|
||||
* Bar charts
|
||||
* Pie charts
|
||||
|
||||
### 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-g6mxioq8-j3iUTF1YKX7D23ML3qcc4g)
|
||||
|
||||
### Installation:
|
||||
|
||||
It requires iOS 13 and Xcode 11!
|
||||
|
||||
In Xcode got to `File -> Swift Packages -> Add Package Dependency` and paste inthe repo's url: `https://github.com/AppPear/ChartView`
|
||||
In Xcode go to `File -> Swift Packages -> Add Package Dependency` and paste in the repo's url: `https://github.com/AppPear/ChartView`
|
||||
|
||||
### Usage:
|
||||
|
||||
@@ -175,6 +188,10 @@ 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:
|
||||
|
||||

|
||||
|
||||
@@ -12,8 +12,12 @@ public struct BarChartRow : View {
|
||||
var data: [Double]
|
||||
var accentColor: Color
|
||||
var gradient: GradientColor?
|
||||
|
||||
var maxValue: Double {
|
||||
data.max() ?? 0
|
||||
guard let max = data.max() else {
|
||||
return 1
|
||||
}
|
||||
return max != 0 ? max : 1
|
||||
}
|
||||
@Binding var touchLocation: CGFloat
|
||||
public var body: some View {
|
||||
@@ -44,7 +48,10 @@ public struct BarChartRow : View {
|
||||
#if DEBUG
|
||||
struct ChartRow_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
BarChartRow(data: [8,23,54,32,12,37,7], accentColor: Colors.OrangeStart, touchLocation: .constant(-1))
|
||||
Group {
|
||||
BarChartRow(data: [0], accentColor: Colors.OrangeStart, touchLocation: .constant(-1))
|
||||
BarChartRow(data: [8,23,54,32,12,37,7], accentColor: Colors.OrangeStart, touchLocation: .constant(-1))
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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 {
|
||||
@@ -50,7 +52,7 @@ public struct BarChartView : View {
|
||||
Rectangle()
|
||||
.fill(self.colorScheme == .dark ? self.darkModeStyle.backgroundColor : self.style.backgroundColor)
|
||||
.cornerRadius(20)
|
||||
.shadow(color: Color.gray, radius: self.dropShadow ? 8 : 0)
|
||||
.shadow(color: self.style.dropShadowColor, radius: self.dropShadow ? 8 : 0)
|
||||
VStack(alignment: .leading){
|
||||
HStack{
|
||||
if(!showValue){
|
||||
@@ -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()
|
||||
|
||||
@@ -34,7 +34,7 @@ public struct GradientColor {
|
||||
public let start: Color
|
||||
public let end: Color
|
||||
|
||||
init(start: Color, end: Color) {
|
||||
public init(start: Color, end: Color) {
|
||||
self.start = start
|
||||
self.end = end
|
||||
}
|
||||
@@ -62,63 +62,72 @@ public struct Styles {
|
||||
accentColor: Colors.OrangeStart,
|
||||
secondGradientColor: Colors.OrangeEnd,
|
||||
textColor: Color.black,
|
||||
legendTextColor: Color.gray)
|
||||
legendTextColor: Color.gray,
|
||||
dropShadowColor: Color.gray)
|
||||
|
||||
public static let barChartStyleOrangeLight = ChartStyle(
|
||||
backgroundColor: Color.white,
|
||||
accentColor: Colors.OrangeStart,
|
||||
secondGradientColor: Colors.OrangeEnd,
|
||||
textColor: Color.black,
|
||||
legendTextColor: Color.gray)
|
||||
legendTextColor: Color.gray,
|
||||
dropShadowColor: Color.gray)
|
||||
|
||||
public static let barChartStyleOrangeDark = ChartStyle(
|
||||
backgroundColor: Color.black,
|
||||
accentColor: Colors.OrangeStart,
|
||||
secondGradientColor: Colors.OrangeEnd,
|
||||
textColor: Color.white,
|
||||
legendTextColor: Color.gray)
|
||||
legendTextColor: Color.gray,
|
||||
dropShadowColor: Color.gray)
|
||||
|
||||
public static let barChartStyleNeonBlueLight = ChartStyle(
|
||||
backgroundColor: Color.white,
|
||||
accentColor: Colors.GradientNeonBlue,
|
||||
secondGradientColor: Colors.GradientPurple,
|
||||
textColor: Color.black,
|
||||
legendTextColor: Color.gray)
|
||||
legendTextColor: Color.gray,
|
||||
dropShadowColor: Color.gray)
|
||||
|
||||
public static let barChartStyleNeonBlueDark = ChartStyle(
|
||||
backgroundColor: Color.black,
|
||||
accentColor: Colors.GradientNeonBlue,
|
||||
secondGradientColor: Colors.GradientPurple,
|
||||
textColor: Color.white,
|
||||
legendTextColor: Color.gray)
|
||||
legendTextColor: Color.gray,
|
||||
dropShadowColor: Color.gray)
|
||||
|
||||
public static let barChartMidnightGreenDark = ChartStyle(
|
||||
backgroundColor: Color(hexString: "#36534D"), //3B5147, 313D34
|
||||
accentColor: Color(hexString: "#FFD603"),
|
||||
secondGradientColor: Color(hexString: "#FFCA04"),
|
||||
textColor: Color.white,
|
||||
legendTextColor: Color(hexString: "#D2E5E1"))
|
||||
legendTextColor: Color(hexString: "#D2E5E1"),
|
||||
dropShadowColor: Color.gray)
|
||||
|
||||
public static let barChartMidnightGreenLight = ChartStyle(
|
||||
backgroundColor: Color.white,
|
||||
accentColor: Color(hexString: "#84A094"), //84A094 , 698378
|
||||
secondGradientColor: Color(hexString: "#50675D"),
|
||||
textColor: Color.black,
|
||||
legendTextColor:Color.gray)
|
||||
legendTextColor:Color.gray,
|
||||
dropShadowColor: Color.gray)
|
||||
|
||||
public static let pieChartStyleOne = ChartStyle(
|
||||
backgroundColor: Color.white,
|
||||
accentColor: Colors.OrangeEnd,
|
||||
secondGradientColor: Colors.OrangeStart,
|
||||
textColor: Color.black,
|
||||
legendTextColor: Color.gray)
|
||||
legendTextColor: Color.gray,
|
||||
dropShadowColor: Color.gray)
|
||||
|
||||
public static let lineViewDarkMode = ChartStyle(
|
||||
backgroundColor: Color.black,
|
||||
accentColor: Colors.OrangeStart,
|
||||
secondGradientColor: Colors.OrangeEnd,
|
||||
textColor: Color.white,
|
||||
legendTextColor: Color.white)
|
||||
legendTextColor: Color.white,
|
||||
dropShadowColor: Color.gray)
|
||||
}
|
||||
|
||||
public struct ChartForm {
|
||||
@@ -126,15 +135,15 @@ public struct ChartForm {
|
||||
public static let small = CGSize(width:120, height:90)
|
||||
public static let medium = CGSize(width:120, height:160)
|
||||
public static let large = CGSize(width:180, height:90)
|
||||
public static let extraLarge = CGSize(width:180, height:90)
|
||||
public static let detail = CGSize(width:180, height:160)
|
||||
#else
|
||||
public static let small = CGSize(width:180, height:120)
|
||||
public static let medium = CGSize(width:180, height:240)
|
||||
public static let large = CGSize(width:360, height:120)
|
||||
public static let extraLarge = CGSize(width:360, height:240)
|
||||
public static let detail = CGSize(width:180, height:120)
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class ChartStyle {
|
||||
@@ -143,22 +152,25 @@ public class ChartStyle {
|
||||
public var gradientColor: GradientColor
|
||||
public var textColor: Color
|
||||
public var legendTextColor: Color
|
||||
public var dropShadowColor: Color
|
||||
public weak var darkModeStyle: ChartStyle?
|
||||
|
||||
public init(backgroundColor: Color, accentColor: Color, secondGradientColor: Color, textColor: Color, legendTextColor: Color){
|
||||
public init(backgroundColor: Color, accentColor: Color, secondGradientColor: Color, textColor: Color, legendTextColor: Color, dropShadowColor: Color){
|
||||
self.backgroundColor = backgroundColor
|
||||
self.accentColor = accentColor
|
||||
self.gradientColor = GradientColor(start: accentColor, end: secondGradientColor)
|
||||
self.textColor = textColor
|
||||
self.legendTextColor = legendTextColor
|
||||
self.dropShadowColor = dropShadowColor
|
||||
}
|
||||
|
||||
public init(backgroundColor: Color, accentColor: Color, gradientColor: GradientColor, textColor: Color, legendTextColor: Color){
|
||||
public init(backgroundColor: Color, accentColor: Color, gradientColor: GradientColor, textColor: Color, legendTextColor: Color, dropShadowColor: Color){
|
||||
self.backgroundColor = backgroundColor
|
||||
self.accentColor = accentColor
|
||||
self.gradientColor = gradientColor
|
||||
self.textColor = textColor
|
||||
self.legendTextColor = legendTextColor
|
||||
self.dropShadowColor = dropShadowColor
|
||||
}
|
||||
|
||||
public init(formSize: CGSize){
|
||||
@@ -167,6 +179,7 @@ public class ChartStyle {
|
||||
self.gradientColor = GradientColors.orange
|
||||
self.legendTextColor = Color.gray
|
||||
self.textColor = Color.black
|
||||
self.dropShadowColor = Color.gray
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,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 {
|
||||
@@ -27,7 +28,7 @@ struct Legend: View {
|
||||
if (min < 0){
|
||||
return (frame.size.height-padding) / CGFloat(max - min)
|
||||
}else{
|
||||
return (frame.size.height-padding) / CGFloat(max + min)
|
||||
return (frame.size.height-padding) / CGFloat(max - min)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
@@ -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)
|
||||
|
||||
@@ -34,7 +34,6 @@ public struct Line: View {
|
||||
if minDataValue != nil && maxDataValue != nil {
|
||||
min = minDataValue!
|
||||
max = maxDataValue!
|
||||
print(min,max)
|
||||
}else if let minPoint = points.min(), let maxPoint = points.max(), minPoint != maxPoint {
|
||||
min = minPoint
|
||||
max = maxPoint
|
||||
@@ -45,7 +44,7 @@ public struct Line: View {
|
||||
if (min <= 0){
|
||||
return (frame.size.height-padding) / CGFloat(max - min)
|
||||
}else{
|
||||
return (frame.size.height-padding) / CGFloat(max + min)
|
||||
return (frame.size.height-padding) / CGFloat(max - min)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
@@ -81,7 +80,6 @@ public struct Line: View {
|
||||
.onDisappear {
|
||||
self.showFull = false
|
||||
}
|
||||
.drawingGroup()
|
||||
if(self.showIndicator) {
|
||||
IndicatorPoint()
|
||||
.position(self.getClosestPointOnPath(touchLocation: self.touchLocation))
|
||||
|
||||
@@ -30,8 +30,8 @@ public struct LineChartView: View {
|
||||
|
||||
}
|
||||
}
|
||||
let frame = CGSize(width: 180, height: 120)
|
||||
private var rateValue: Int
|
||||
var frame = CGSize(width: 180, height: 120)
|
||||
private var rateValue: Int?
|
||||
|
||||
public init(data: [Double],
|
||||
title: String,
|
||||
@@ -48,9 +48,10 @@ public struct LineChartView: View {
|
||||
self.style = style
|
||||
self.darkModeStyle = style.darkModeStyle != nil ? style.darkModeStyle! : Styles.lineViewDarkMode
|
||||
self.formSize = form!
|
||||
self.rateValue = rateValue!
|
||||
frame = CGSize(width: self.formSize.width, height: self.formSize.height/2)
|
||||
self.dropShadow = dropShadow!
|
||||
self.valueSpecifier = valueSpecifier!
|
||||
self.rateValue = rateValue
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
@@ -58,7 +59,7 @@ public struct LineChartView: View {
|
||||
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)
|
||||
.shadow(color: self.style.dropShadowColor, radius: self.dropShadow ? 8 : 0)
|
||||
VStack(alignment: .leading){
|
||||
if(!self.showIndicatorDot){
|
||||
VStack(alignment: .leading, spacing: 8){
|
||||
@@ -72,12 +73,16 @@ public struct LineChartView: View {
|
||||
.foregroundColor(self.colorScheme == .dark ? self.darkModeStyle.legendTextColor :self.style.legendTextColor)
|
||||
}
|
||||
HStack {
|
||||
if (self.rateValue >= 0){
|
||||
Image(systemName: "arrow.up")
|
||||
}else{
|
||||
Image(systemName: "arrow.down")
|
||||
|
||||
if (self.rateValue ?? 0 != 0)
|
||||
{
|
||||
if (self.rateValue ?? 0 >= 0){
|
||||
Image(systemName: "arrow.up")
|
||||
}else{
|
||||
Image(systemName: "arrow.down")
|
||||
}
|
||||
Text("\(self.rateValue!)%")
|
||||
}
|
||||
Text("\(self.rateValue)%")
|
||||
}
|
||||
}
|
||||
.transition(.opacity)
|
||||
@@ -96,14 +101,14 @@ public struct LineChartView: View {
|
||||
Spacer()
|
||||
GeometryReader{ geometry in
|
||||
Line(data: self.data,
|
||||
frame: .constant(geometry.frame(in: .local)),
|
||||
touchLocation: self.$touchLocation,
|
||||
showIndicator: self.$showIndicatorDot,
|
||||
minDataValue: .constant(nil),
|
||||
maxDataValue: .constant(nil)
|
||||
frame: .constant(geometry.frame(in: .local)),
|
||||
touchLocation: self.$touchLocation,
|
||||
showIndicator: self.$showIndicatorDot,
|
||||
minDataValue: .constant(nil),
|
||||
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)
|
||||
@@ -139,6 +144,9 @@ struct WidgetView_Previews: PreviewProvider {
|
||||
Group {
|
||||
LineChartView(data: [8,23,54,32,12,37,7,23,43], title: "Line chart", legend: "Basic")
|
||||
.environment(\.colorScheme, .light)
|
||||
|
||||
LineChartView(data: [282.502, 284.495, 283.51, 285.019, 285.197, 286.118, 288.737, 288.455, 289.391, 287.691, 285.878, 286.46, 286.252, 284.652, 284.129, 284.188], title: "Line chart", legend: "Basic")
|
||||
.environment(\.colorScheme, .light)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,17 +63,18 @@ 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),
|
||||
maxDataValue: .constant(nil),
|
||||
showBackground: false
|
||||
showBackground: false,
|
||||
gradient: self.style.gradientColor
|
||||
)
|
||||
.offset(x: 30, y: 0)
|
||||
.onAppear(){
|
||||
@@ -120,7 +124,12 @@ public struct LineView: View {
|
||||
|
||||
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)
|
||||
Group {
|
||||
LineView(data: [8,23,54,32,12,37,7,23,43], title: "Full chart", style: Styles.lineChartStyleOne)
|
||||
|
||||
LineView(data: [282.502, 284.495, 283.51, 285.019, 285.197, 286.118, 288.737, 288.455, 289.391, 287.691, 285.878, 286.46, 286.252, 284.652, 284.129, 284.188], title: "Full chart", style: Styles.lineChartStyleOne)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,5 +29,6 @@ public struct MagnifierRect: View {
|
||||
.blendMode(.multiply)
|
||||
}
|
||||
}
|
||||
.offset(x: 0, y: -15)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ public struct MultiLineChartView: View {
|
||||
public var legend: String?
|
||||
public var style: ChartStyle
|
||||
public var darkModeStyle: ChartStyle
|
||||
public var formSize:CGSize
|
||||
public var formSize: CGSize
|
||||
public var dropShadow: Bool
|
||||
public var valueSpecifier:String
|
||||
|
||||
@@ -43,27 +43,28 @@ public struct MultiLineChartView: View {
|
||||
return 0
|
||||
}
|
||||
|
||||
let frame = CGSize(width: 180, height: 120)
|
||||
private var rateValue: Int
|
||||
var frame = CGSize(width: 180, height: 120)
|
||||
private var rateValue: Int?
|
||||
|
||||
public init(data: [([Double], GradientColor)],
|
||||
title: String,
|
||||
legend: String? = nil,
|
||||
style: ChartStyle = Styles.lineChartStyleOne,
|
||||
form: CGSize? = ChartForm.medium,
|
||||
rateValue: Int? = 14,
|
||||
dropShadow: Bool? = true,
|
||||
valueSpecifier: String? = "%.1f") {
|
||||
form: CGSize = ChartForm.medium,
|
||||
rateValue: Int? = nil,
|
||||
dropShadow: Bool = true,
|
||||
valueSpecifier: String = "%.1f") {
|
||||
|
||||
self.data = data.map({ MultiLineChartData(points: $0.0, gradient: $0.1)})
|
||||
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!
|
||||
self.valueSpecifier = valueSpecifier!
|
||||
self.formSize = form
|
||||
frame = CGSize(width: self.formSize.width, height: self.formSize.height/2)
|
||||
self.rateValue = rateValue
|
||||
self.dropShadow = dropShadow
|
||||
self.valueSpecifier = valueSpecifier
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
@@ -85,12 +86,12 @@ public struct MultiLineChartView: View {
|
||||
.foregroundColor(self.colorScheme == .dark ? self.darkModeStyle.legendTextColor : self.style.legendTextColor)
|
||||
}
|
||||
HStack {
|
||||
if (self.rateValue >= 0){
|
||||
if (rateValue ?? 0 >= 0){
|
||||
Image(systemName: "arrow.up")
|
||||
}else{
|
||||
Image(systemName: "arrow.down")
|
||||
}
|
||||
Text("\(self.rateValue)%")
|
||||
Text("\(rateValue ?? 0)%")
|
||||
}
|
||||
}
|
||||
.transition(.opacity)
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// File.swift
|
||||
//
|
||||
//
|
||||
// Created by 曾文志 on 2020/7/30.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
func isPointInCircle(point: CGPoint, circleRect: CGRect) -> Bool {
|
||||
let r = min(circleRect.width, circleRect.height) / 2
|
||||
let center = CGPoint(x: circleRect.midX, y: circleRect.midY)
|
||||
let dx = point.x - center.x
|
||||
let dy = point.y - center.y
|
||||
let distance = sqrt(dx * dx + dy * dy)
|
||||
return distance <= r
|
||||
}
|
||||
|
||||
func degree(for point: CGPoint, inCircleRect circleRect: CGRect) -> Double {
|
||||
let center = CGPoint(x: circleRect.midX, y: circleRect.midY)
|
||||
let dx = point.x - center.x
|
||||
let dy = point.y - center.y
|
||||
let acuteDegree = Double(atan(dy / dx)) * (180 / .pi)
|
||||
|
||||
let isInBottomRight = dx >= 0 && dy >= 0
|
||||
let isInBottomLeft = dx <= 0 && dy >= 0
|
||||
let isInTopLeft = dx <= 0 && dy <= 0
|
||||
let isInTopRight = dx >= 0 && dy <= 0
|
||||
|
||||
if isInBottomRight {
|
||||
return acuteDegree
|
||||
} else if isInBottomLeft {
|
||||
return 180 - abs(acuteDegree)
|
||||
} else if isInTopLeft {
|
||||
return 180 + abs(acuteDegree)
|
||||
} else if isInTopRight {
|
||||
return 360 - abs(acuteDegree)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -25,13 +25,42 @@ public struct PieChartRow : View {
|
||||
}
|
||||
return tempSlices
|
||||
}
|
||||
|
||||
@Binding var showValue: Bool
|
||||
@Binding var currentValue: Double
|
||||
|
||||
@State private var currentTouchedIndex = -1 {
|
||||
didSet {
|
||||
if oldValue != currentTouchedIndex {
|
||||
showValue = currentTouchedIndex != -1
|
||||
currentValue = showValue ? slices[currentTouchedIndex].value : 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
GeometryReader { geometry in
|
||||
ZStack{
|
||||
ForEach(0..<self.slices.count){ i in
|
||||
PieChartCell(rect: geometry.frame(in: .local), startDeg: self.slices[i].startDeg, endDeg: self.slices[i].endDeg, index: i, backgroundColor: self.backgroundColor,accentColor: self.accentColor)
|
||||
.scaleEffect(self.currentTouchedIndex == i ? 1.1 : 1)
|
||||
.animation(Animation.spring())
|
||||
}
|
||||
}
|
||||
.gesture(DragGesture()
|
||||
.onChanged({ value in
|
||||
let rect = geometry.frame(in: .local)
|
||||
let isTouchInPie = isPointInCircle(point: value.location, circleRect: rect)
|
||||
if isTouchInPie {
|
||||
let touchDegree = degree(for: value.location, inCircleRect: rect)
|
||||
self.currentTouchedIndex = self.slices.firstIndex(where: { $0.startDeg < touchDegree && $0.endDeg > touchDegree }) ?? -1
|
||||
} else {
|
||||
self.currentTouchedIndex = -1
|
||||
}
|
||||
})
|
||||
.onEnded({ value in
|
||||
self.currentTouchedIndex = -1
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,8 +68,12 @@ public struct PieChartRow : View {
|
||||
#if DEBUG
|
||||
struct PieChartRow_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
PieChartRow(data:[8,23,54,32,12,37,7,23,43], backgroundColor: Color(red: 252.0/255.0, green: 236.0/255.0, blue: 234.0/255.0), accentColor: Color(red: 225.0/255.0, green: 97.0/255.0, blue: 76.0/255.0)).frame(width: 100, height: 100)
|
||||
|
||||
Group {
|
||||
PieChartRow(data:[8,23,54,32,12,37,7,23,43], backgroundColor: Color(red: 252.0/255.0, green: 236.0/255.0, blue: 234.0/255.0), accentColor: Color(red: 225.0/255.0, green: 97.0/255.0, blue: 76.0/255.0), showValue: Binding.constant(false), currentValue: Binding.constant(0))
|
||||
.frame(width: 100, height: 100)
|
||||
PieChartRow(data:[0], backgroundColor: Color(red: 252.0/255.0, green: 236.0/255.0, blue: 234.0/255.0), accentColor: Color(red: 225.0/255.0, green: 97.0/255.0, blue: 76.0/255.0), showValue: Binding.constant(false), currentValue: Binding.constant(0))
|
||||
.frame(width: 100, height: 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -15,14 +15,28 @@ public struct PieChartView : View {
|
||||
public var style: ChartStyle
|
||||
public var formSize:CGSize
|
||||
public var dropShadow: Bool
|
||||
|
||||
public init(data: [Double], title: String, legend: String? = nil, style: ChartStyle = Styles.pieChartStyleOne, form: CGSize? = ChartForm.medium, dropShadow: Bool? = true ){
|
||||
public var valueSpecifier:String
|
||||
|
||||
@State private var showValue = false
|
||||
@State private var currentValue: Double = 0 {
|
||||
didSet{
|
||||
if(oldValue != self.currentValue && self.showValue) {
|
||||
HapticFeedback.playSelection()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public init(data: [Double], title: String, legend: String? = nil, style: ChartStyle = Styles.pieChartStyleOne, form: CGSize? = ChartForm.medium, dropShadow: Bool? = true, valueSpecifier: String? = "%.1f"){
|
||||
self.data = data
|
||||
self.title = title
|
||||
self.legend = legend
|
||||
self.style = style
|
||||
self.formSize = form!
|
||||
if self.formSize == ChartForm.large {
|
||||
self.formSize = ChartForm.extraLarge
|
||||
}
|
||||
self.dropShadow = dropShadow!
|
||||
self.valueSpecifier = valueSpecifier!
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
@@ -30,18 +44,24 @@ public struct PieChartView : View {
|
||||
Rectangle()
|
||||
.fill(self.style.backgroundColor)
|
||||
.cornerRadius(20)
|
||||
.shadow(color: Color.gray, radius: self.dropShadow ? 12 : 0)
|
||||
.shadow(color: self.style.dropShadowColor, radius: self.dropShadow ? 12 : 0)
|
||||
VStack(alignment: .leading){
|
||||
HStack{
|
||||
Text(self.title)
|
||||
.font(.headline)
|
||||
.foregroundColor(self.style.textColor)
|
||||
if(!showValue){
|
||||
Text(self.title)
|
||||
.font(.headline)
|
||||
.foregroundColor(self.style.textColor)
|
||||
}else{
|
||||
Text("\(self.currentValue, specifier: self.valueSpecifier)")
|
||||
.font(.headline)
|
||||
.foregroundColor(self.style.textColor)
|
||||
}
|
||||
Spacer()
|
||||
Image(systemName: "chart.pie.fill")
|
||||
.imageScale(.large)
|
||||
.foregroundColor(self.style.legendTextColor)
|
||||
}.padding()
|
||||
PieChartRow(data: data, backgroundColor: self.style.backgroundColor, accentColor: self.style.accentColor)
|
||||
PieChartRow(data: data, backgroundColor: self.style.backgroundColor, accentColor: self.style.accentColor, showValue: $showValue, currentValue: $currentValue)
|
||||
.foregroundColor(self.style.accentColor).padding(self.legend != nil ? 0 : 12).offset(y:self.legend != nil ? 0 : -10)
|
||||
if(self.legend != nil) {
|
||||
Text(self.legend!)
|
||||
|
||||
Reference in New Issue
Block a user