Prepare charts to display x and y values souch as a value for a given point

This commit is contained in:
Andras Samu
2020-08-24 16:41:31 +02:00
parent c843e6bede
commit 422c5c0303
7 changed files with 39 additions and 25 deletions
@@ -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
}
@@ -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)
@@ -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
@@ -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]
}
}
@@ -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]
}
}
@@ -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)
@@ -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]
}
}