From 422c5c03032bcf807df3bb97b0e84bda5b64f6bd Mon Sep 17 00:00:00 2001 From: Andras Samu Date: Mon, 24 Aug 2020 16:41:31 +0200 Subject: [PATCH] Prepare charts to display x and y values souch as a value for a given point --- .../SwiftUICharts/Base/Chart/ChartData.swift | 14 ++++++++++- .../Base/Extensions/ChartBase+Extension.swift | 7 ++++++ .../SwiftUICharts/Base/Label/ChartLabel.swift | 4 --- .../Charts/BarChart/BarChartRow.swift | 6 ++--- .../SwiftUICharts/Charts/LineChart/Line.swift | 25 +++++++++---------- .../Charts/PieChart/PieChartRow.swift | 4 +-- .../Charts/RingsChart/RingsChartRow.swift | 4 +-- 7 files changed, 39 insertions(+), 25 deletions(-) diff --git a/Sources/SwiftUICharts/Base/Chart/ChartData.swift b/Sources/SwiftUICharts/Base/Chart/ChartData.swift index 61beec7..28c2065 100644 --- a/Sources/SwiftUICharts/Base/Chart/ChartData.swift +++ b/Sources/SwiftUICharts/Base/Chart/ChartData.swift @@ -2,11 +2,23 @@ import SwiftUI /// An observable wrapper for an array of data for use in any chart public class ChartData: ObservableObject { - @Published public var data: [Double] = [] + @Published public var data: [(String, Double)] = [] + + var points: [Double] { + data.map { $0.1 } + } + + var values: [String] { + data.map { $0.0 } + } /// Initialize with data array /// - Parameter data: Array of `Double` public init(_ data: [Double]) { + self.data = data.map { ("", $0) } + } + + public init(_ data: [(String, Double)]) { self.data = data } diff --git a/Sources/SwiftUICharts/Base/Extensions/ChartBase+Extension.swift b/Sources/SwiftUICharts/Base/Extensions/ChartBase+Extension.swift index 90060e3..73873d7 100644 --- a/Sources/SwiftUICharts/Base/Extensions/ChartBase+Extension.swift +++ b/Sources/SwiftUICharts/Base/Extensions/ChartBase+Extension.swift @@ -6,6 +6,13 @@ extension View where Self: ChartBase { /// - Parameter data: array of `Double` /// - Returns: modified `View` with data attached public func data(_ data: [Double]) -> some View { + chartData.data = data.map { ("", $0) } + return self + .environmentObject(chartData) + .environmentObject(ChartValue()) + } + + public func data(_ data: [(String, Double)]) -> some View { chartData.data = data return self .environmentObject(chartData) diff --git a/Sources/SwiftUICharts/Base/Label/ChartLabel.swift b/Sources/SwiftUICharts/Base/Label/ChartLabel.swift index 37d97e5..96a8630 100644 --- a/Sources/SwiftUICharts/Base/Label/ChartLabel.swift +++ b/Sources/SwiftUICharts/Base/Label/ChartLabel.swift @@ -12,12 +12,8 @@ public enum ChartLabelType { /// A chart may contain any number of labels in pre-set positions based on their `ChartLabelType` public struct ChartLabel: View { @EnvironmentObject var chartValue: ChartValue -<<<<<<< HEAD - @State private var textToDisplay:String = "" -======= @State var textToDisplay:String = "" var format: String = "%.01f" ->>>>>>> Add custom string format for ChartLabel when interactionInProgress = true (#151) private var title: String diff --git a/Sources/SwiftUICharts/Charts/BarChart/BarChartRow.swift b/Sources/SwiftUICharts/Charts/BarChart/BarChartRow.swift index 4ab28f4..366a331 100644 --- a/Sources/SwiftUICharts/Charts/BarChart/BarChartRow.swift +++ b/Sources/SwiftUICharts/Charts/BarChart/BarChartRow.swift @@ -13,7 +13,7 @@ public struct BarChartRow: View { var style: ChartStyle var maxValue: Double { - guard let max = chartData.data.max() else { + guard let max = chartData.points.max() else { return 1 } return max != 0 ? max : 1 @@ -62,7 +62,7 @@ public struct BarChartRow: View { /// - Parameter index: index into array of data /// - Returns: data value at given index, divided by data maximum func normalizedValue(index: Int) -> Double { - return Double(chartData.data[index])/Double(maxValue) + return Double(chartData.points[index])/Double(maxValue) } /// Size to scale the touch indicator @@ -84,6 +84,6 @@ public struct BarChartRow: View { func getCurrentValue(width: CGFloat) -> Double? { guard self.chartData.data.count > 0 else { return nil} let index = max(0,min(self.chartData.data.count-1,Int(floor((self.touchLocation*width)/(width/CGFloat(self.chartData.data.count)))))) - return self.chartData.data[index] + return self.chartData.points[index] } } diff --git a/Sources/SwiftUICharts/Charts/LineChart/Line.swift b/Sources/SwiftUICharts/Charts/LineChart/Line.swift index d94bf6a..942342d 100644 --- a/Sources/SwiftUICharts/Charts/LineChart/Line.swift +++ b/Sources/SwiftUICharts/Charts/LineChart/Line.swift @@ -16,14 +16,14 @@ public struct Line: View { /// Step for plotting through data /// - Returns: X and Y delta between each data point based on data and view's frame - var step: CGPoint { - return CGPoint.getStep(frame: frame, data: chartData.data) + var step: CGPoint { + return CGPoint.getStep(frame: frame, data: chartData.points) } /// Path of line graph /// - Returns: A path for stroking representing the data, either curved or jagged. var path: Path { - let points = chartData.data + let points = chartData.points if curvedLines { return Path.quadCurvedPathWithPoints(points: points, @@ -37,7 +37,7 @@ public struct Line: View { /// Path of linegraph, but also closed at the bottom side /// - Returns: A path for filling representing the data, either curved or jagged var closedPath: Path { - let points = chartData.data + let points = chartData.points if curvedLines { return Path.quadClosedCurvedPathWithPoints(points: points, @@ -47,20 +47,19 @@ public struct Line: View { return Path.closedLinePathWithPoints(points: points, step: step) } + + // see https://stackoverflow.com/a/62370919 + // This lets geometry be recalculated when device rotates. However it doesn't cover issue of app changing + // from full screen to split view. Not possible in SwiftUI? Feedback submitted to apple FB8451194. + let orientationChanged = NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification) + .makeConnectable() + .autoconnect() /// The content and behavior of the `Line`. - /// /// Draw the background if showing the full line (?) and the `showBackground` option is set. Above that draw the line, and then the data indicator if the graph is currently being touched. /// On appear, set the frame so that the data graph metrics can be calculated. On a drag (touch) gesture, highlight the closest touched data point. /// TODO: explain rotation public var body: some View { - - let orientationChanged = NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification) - .makeConnectable() - .autoconnect() // see https://stackoverflow.com/a/62370919 - // This lets geometry be recalculated when device rotates. However it doesn't cover issue of app changing - // from full screen to split view. Not possible in SwiftUI? Feedback submitted to apple FB8451194. - GeometryReader { geometry in ZStack { if self.showFull && self.showBackground { @@ -120,7 +119,7 @@ extension Line { private func getClosestDataPoint(point: CGPoint) { let index = Int(round((point.x)/step.x)) if (index >= 0 && index < self.chartData.data.count){ - self.chartValue.currentValue = self.chartData.data[index] + self.chartValue.currentValue = self.chartData.points[index] } } diff --git a/Sources/SwiftUICharts/Charts/PieChart/PieChartRow.swift b/Sources/SwiftUICharts/Charts/PieChart/PieChartRow.swift index eeed076..f812a17 100644 --- a/Sources/SwiftUICharts/Charts/PieChart/PieChartRow.swift +++ b/Sources/SwiftUICharts/Charts/PieChart/PieChartRow.swift @@ -10,9 +10,9 @@ public struct PieChartRow: View { var slices: [PieSlice] { var tempSlices: [PieSlice] = [] var lastEndDeg: Double = 0 - let maxValue: Double = chartData.data.reduce(0, +) + let maxValue: Double = chartData.points.reduce(0, +) - for slice in chartData.data { + for slice in chartData.points { let normalized: Double = Double(slice) / (maxValue == 0 ? 1 : maxValue) let startDeg = lastEndDeg let endDeg = lastEndDeg + (normalized * 360) diff --git a/Sources/SwiftUICharts/Charts/RingsChart/RingsChartRow.swift b/Sources/SwiftUICharts/Charts/RingsChart/RingsChartRow.swift index cbdbfbd..7e2dd82 100644 --- a/Sources/SwiftUICharts/Charts/RingsChart/RingsChartRow.swift +++ b/Sources/SwiftUICharts/Charts/RingsChart/RingsChartRow.swift @@ -39,7 +39,7 @@ public struct RingsChartRow: View { // make sure it doesn't get to crazy value ) - Ring(ringWidth:scaledWidth, percent: self.chartData.data[index], foregroundColor:self.style.foregroundColor.rotate(for: index), + Ring(ringWidth:scaledWidth, percent: self.chartData.points[index], foregroundColor:self.style.foregroundColor.rotate(for: index), touchLocation: self.touchRadius) @@ -108,7 +108,7 @@ public struct RingsChartRow: View { func getCurrentValue(maxRadius: CGFloat) -> Double? { guard let index = self.touchedCircleIndex(maxRadius: maxRadius) else { return nil } - return self.chartData.data[index] + return self.chartData.points[index] } }