Mercurial > public > stock-charts
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 } |