comparison 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
comparison
equal deleted inserted replaced
20:24dfde3727c1 21:5135ff3343ae
1 //
2 // LineView.swift
3 // StockCharts
4 //
5 // Created by Dennis Concepción Martín on 30/4/21.
6 //
7
8 import SwiftUI
9
10 public struct LineView: View {
11 var data: [Double]
12 var dates: [String]?
13 var hours: [String]?
14
15 @Binding var showingIndicators: Bool
16 @Binding var indexPosition: Int
17 @State var IndicatorPointPosition: CGPoint = .zero
18 @State var pathPoints = [CGPoint]()
19
20 public var body: some View {
21 ZStack {
22 GeometryReader { proxy in
23 LinePath(data: data, width: proxy.size.width, height: proxy.size.height, pathPoints: $pathPoints)
24 .stroke(colorLine(), lineWidth: 2)
25 }
26
27 if showingIndicators {
28 IndicatorPoint()
29 .position(x: IndicatorPointPosition.x, y: IndicatorPointPosition.y)
30 }
31 }
32 .rotationEffect(.degrees(180), anchor: .center)
33 .rotation3DEffect(.degrees(180), axis: (x: 0.0, y: 1.0, z: 0.0))
34 .contentShape(Rectangle()) // Control tappable area
35 .gesture(
36 LongPressGesture(minimumDuration: 0.2)
37 .sequenced(before: DragGesture(minimumDistance: 0, coordinateSpace: .local))
38 .onChanged({ value in // Get value of the gesture
39 switch value {
40 case .second(true, let drag):
41 if let longPressLocation = drag?.location {
42 dragGesture(longPressLocation)
43 }
44 default:
45 break
46 }
47 })
48 // Hide indicator when finish
49 .onEnded({ value in
50 self.showingIndicators = false
51 })
52 )
53 }
54
55 /*
56 Color path depending on data.
57 */
58 public func colorLine() -> Color {
59 var color = Color(.systemGreen)
60
61 if data.first! > data.last! {
62 color = Color(.systemRed)
63 } else if data.first! == data.last! {
64 color = Color(.systemTeal)
65 }
66 else if showingIndicators {
67 color = Color(.systemBlue)
68 }
69
70 return color
71 }
72
73 /*
74 When the user drag on Path -> Modifiy indicator point to move it on the path accordingly
75 */
76 public func dragGesture(_ longPressLocation: CGPoint) {
77 let (closestXPoint, closestYPoint, yPointIndex) = getClosestValueFrom(longPressLocation, inData: pathPoints)
78 self.IndicatorPointPosition.x = closestXPoint
79 self.IndicatorPointPosition.y = closestYPoint
80 self.showingIndicators = true
81 self.indexPosition = yPointIndex
82 }
83
84 /*
85 First, search the closest X point in Path from the tapped location.
86 Then, find the correspondent Y point in Path.
87 */
88 public func getClosestValueFrom(_ value: CGPoint, inData: [CGPoint]) -> (CGFloat, CGFloat, Int) {
89 let touchPoint: (CGFloat, CGFloat) = (value.x, value.y)
90 let xPathPoints = inData.map { $0.x }
91 let yPathPoints = inData.map { $0.y }
92
93 // Closest X value
94 let closestXPoint = xPathPoints.enumerated().min( by: { abs($0.1 - touchPoint.0) < abs($1.1 - touchPoint.0) } )!
95 let closestYPointIndex = xPathPoints.firstIndex(of: closestXPoint.element)!
96 let closestYPoint = yPathPoints[closestYPointIndex]
97
98 // Index of the closest points in the array
99 let yPointIndex = yPathPoints.firstIndex(of: closestYPoint)!
100
101 return (closestXPoint.element, closestYPoint, yPointIndex)
102 }
103 }