Mercurial > public > stock-charts
diff InteractiveCharts/LineChart/Helpers/LineView.swift @ 8:959175ee5ebd
Implement interaction with ChartView
author | Dennis Concepción Martín <66180929+denniscm190@users.noreply.github.com> |
---|---|
date | Mon, 26 Apr 2021 23:06:42 +0200 |
parents | f828c7c408d4 |
children |
line wrap: on
line diff
--- a/InteractiveCharts/LineChart/Helpers/LineView.swift Mon Apr 26 23:06:25 2021 +0200 +++ b/InteractiveCharts/LineChart/Helpers/LineView.swift Mon Apr 26 23:06:42 2021 +0200 @@ -9,14 +9,47 @@ struct LineView: View { var data: [Double] + var dates: [String]? + var hours: [String]? + + @Binding var showingIndicators: Bool + @Binding var indexPosition: Int + @State private var IndicatorPointPosition: CGPoint = .zero + @State private var pathPoints = [CGPoint]() var body: some View { - GeometryReader { proxy in - LinePath(data: data, width: proxy.size.width, height: proxy.size.height) - .stroke(colorLine(), lineWidth: 2) - .rotationEffect(.degrees(180), anchor: .center) - .rotation3DEffect(.degrees(180), axis: (x: 0.0, y: 1.0, z: 0.0)) + 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 + }) + ) } /* @@ -30,7 +63,41 @@ } 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 + */ + private 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. + */ + private 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) + } }