Mercurial > public > stock-charts
diff Sources/StockCharts/LineChart/Helpers/LineView.swift @ 21:5135ff3343ae
Rename project to StockCharts
author | Dennis Concepción Martín <66180929+denniscm190@users.noreply.github.com> |
---|---|
date | Fri, 30 Apr 2021 17:40:33 +0200 |
parents | Sources/InteractiveCharts/LineChart/Helpers/LineView.swift@24dfde3727c1 |
children | 127af64e264e |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sources/StockCharts/LineChart/Helpers/LineView.swift Fri Apr 30 17:40:33 2021 +0200 @@ -0,0 +1,103 @@ +// +// LineView.swift +// StockCharts +// +// Created by Dennis Concepción Martín on 30/4/21. +// + +import SwiftUI + +public struct LineView: View { + var data: [Double] + var dates: [String]? + var hours: [String]? + + @Binding var showingIndicators: Bool + @Binding var indexPosition: Int + @State var IndicatorPointPosition: CGPoint = .zero + @State var pathPoints = [CGPoint]() + + public var body: some View { + ZStack { + GeometryReader { proxy in + LinePath(data: data, width: proxy.size.width, height: proxy.size.height, pathPoints: $pathPoints) + .stroke(colorLine(), lineWidth: 2) + } + + if showingIndicators { + IndicatorPoint() + .position(x: IndicatorPointPosition.x, y: IndicatorPointPosition.y) + } + } + .rotationEffect(.degrees(180), anchor: .center) + .rotation3DEffect(.degrees(180), axis: (x: 0.0, y: 1.0, z: 0.0)) + .contentShape(Rectangle()) // Control tappable area + .gesture( + LongPressGesture(minimumDuration: 0.2) + .sequenced(before: DragGesture(minimumDistance: 0, coordinateSpace: .local)) + .onChanged({ value in // Get value of the gesture + switch value { + case .second(true, let drag): + if let longPressLocation = drag?.location { + dragGesture(longPressLocation) + } + default: + break + } + }) + // Hide indicator when finish + .onEnded({ value in + self.showingIndicators = false + }) + ) + } + + /* + Color path depending on data. + */ + public func colorLine() -> Color { + var color = Color(.systemGreen) + + if data.first! > data.last! { + color = Color(.systemRed) + } else if data.first! == data.last! { + color = Color(.systemTeal) + } + else if showingIndicators { + color = Color(.systemBlue) + } + + return color + } + + /* + When the user drag on Path -> Modifiy indicator point to move it on the path accordingly + */ + public func dragGesture(_ longPressLocation: CGPoint) { + let (closestXPoint, closestYPoint, yPointIndex) = getClosestValueFrom(longPressLocation, inData: pathPoints) + self.IndicatorPointPosition.x = closestXPoint + self.IndicatorPointPosition.y = closestYPoint + self.showingIndicators = true + self.indexPosition = yPointIndex + } + + /* + First, search the closest X point in Path from the tapped location. + Then, find the correspondent Y point in Path. + */ + public func getClosestValueFrom(_ value: CGPoint, inData: [CGPoint]) -> (CGFloat, CGFloat, Int) { + let touchPoint: (CGFloat, CGFloat) = (value.x, value.y) + let xPathPoints = inData.map { $0.x } + let yPathPoints = inData.map { $0.y } + + // Closest X value + let closestXPoint = xPathPoints.enumerated().min( by: { abs($0.1 - touchPoint.0) < abs($1.1 - touchPoint.0) } )! + let closestYPointIndex = xPathPoints.firstIndex(of: closestXPoint.element)! + let closestYPoint = yPathPoints[closestYPointIndex] + + // Index of the closest points in the array + let yPointIndex = yPathPoints.firstIndex(of: closestYPoint)! + + return (closestXPoint.element, closestYPoint, yPointIndex) + } +}