# HG changeset patch # User Dennis Concepción Martín <66180929+denniscm190@users.noreply.github.com> # Date 1619629300 -7200 # Node ID edf2bfcd8d979f83581a7f1004f8acba9fdb88db # Parent fdab6510dc469b14ab08f668c8006c572de77b04 Reorganise structure diff -r fdab6510dc46 -r edf2bfcd8d97 InteractiveCharts.xcodeproj/project.pbxproj --- a/InteractiveCharts.xcodeproj/project.pbxproj Wed Apr 28 18:58:15 2021 +0200 +++ b/InteractiveCharts.xcodeproj/project.pbxproj Wed Apr 28 19:01:40 2021 +0200 @@ -87,6 +87,15 @@ path = Helpers; sourceTree = ""; }; + 950EFF0D2639CC4D00CE1B7B /* Sources */ = { + isa = PBXGroup; + children = ( + 9557883B2636B8D800D1192D /* InteractiveCharts */, + 955788462636B8D800D1192D /* InteractiveChartsTests */, + ); + path = Sources; + sourceTree = ""; + }; 951D9BDE26375DDA006B6A6D /* UI Previews */ = { isa = PBXGroup; children = ( @@ -109,8 +118,7 @@ isa = PBXGroup; children = ( 950EFF0C2639CAB300CE1B7B /* Package.swift */, - 9557883B2636B8D800D1192D /* InteractiveCharts */, - 955788462636B8D800D1192D /* InteractiveChartsTests */, + 950EFF0D2639CC4D00CE1B7B /* Sources */, 9557883A2636B8D800D1192D /* Products */, ); sourceTree = ""; diff -r fdab6510dc46 -r edf2bfcd8d97 InteractiveCharts.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate Binary file InteractiveCharts.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate has changed diff -r fdab6510dc46 -r edf2bfcd8d97 InteractiveCharts/Info.plist --- a/InteractiveCharts/Info.plist Wed Apr 28 18:58:15 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - - diff -r fdab6510dc46 -r edf2bfcd8d97 InteractiveCharts/InteractiveCharts.h --- a/InteractiveCharts/InteractiveCharts.h Wed Apr 28 18:58:15 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -// -// InteractiveCharts.h -// InteractiveCharts -// -// Created by Dennis Concepción Martín on 26/4/21. -// - -#import - -//! Project version number for InteractiveCharts. -FOUNDATION_EXPORT double InteractiveChartsVersionNumber; - -//! Project version string for InteractiveCharts. -FOUNDATION_EXPORT const unsigned char InteractiveChartsVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import - - diff -r fdab6510dc46 -r edf2bfcd8d97 InteractiveCharts/LineChart/ChartView.swift --- a/InteractiveCharts/LineChart/ChartView.swift Wed Apr 28 18:58:15 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -// -// ChartView.swift -// InteractiveCharts -// -// Created by Dennis Concepción Martín on 26/4/21. -// - -import SwiftUI - -struct ChartView: View { - var data: [Double] - var dates: [String]? - var hours: [String]? - - @State private var showingIndicators = false - @State private var indexPosition = Int() - - var body: some View { - VStack { - ChartLabel(data: data, dates: dates, hours: hours, indexPosition: $indexPosition) - .opacity(showingIndicators ? 1: 0) - .padding(.vertical) - - LineView(data: data, showingIndicators: $showingIndicators, indexPosition: $indexPosition) - } - } -} diff -r fdab6510dc46 -r edf2bfcd8d97 InteractiveCharts/LineChart/Helpers/ChartLabel.swift --- a/InteractiveCharts/LineChart/Helpers/ChartLabel.swift Wed Apr 28 18:58:15 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -// -// ChartLabel.swift -// InteractiveCharts -// -// Created by Dennis Concepción Martín on 26/4/21. -// - -import SwiftUI - -struct ChartLabel: View { - var data: [Double] - var dates: [String]? - var hours: [String]? - - @Binding var indexPosition: Int // Data point position - - var body: some View { - HStack { - Group { - if let dates = self.dates { - let date = formatStringDate(dates[indexPosition]) - Text(date) - } - if let hours = self.hours { - let hour = hours[indexPosition] - Text(hour) - } - Text("\(data[indexPosition], specifier: "%.2f")") - .foregroundColor(Color(.systemBlue)) - } - .font(.headline) - } - } - - /* - Take string in format yy-MM-dd (2021-01-01) and transform it - to long default string format - */ - private func formatStringDate(_ stringDate: String) -> String { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yy-MM-dd" - let date = dateFormatter.date(from: stringDate) - - // Format date to the final format - dateFormatter.dateStyle = .long - let finalDate = dateFormatter.string(from: date!) - - return finalDate - } -} diff -r fdab6510dc46 -r edf2bfcd8d97 InteractiveCharts/LineChart/Helpers/IndicatorPoint.swift --- a/InteractiveCharts/LineChart/Helpers/IndicatorPoint.swift Wed Apr 28 18:58:15 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -// -// IndicatorPoint.swift -// InteractiveCharts -// -// Created by Dennis Concepción Martín on 26/4/21. -// - -import SwiftUI - -struct IndicatorPoint: View { - var body: some View { - Circle() - .frame(width: 20, height: 20) - .foregroundColor(Color(.systemBlue)) - - } -} - -struct IndicatorPoint_Previews: PreviewProvider { - static var previews: some View { - IndicatorPoint() - } -} diff -r fdab6510dc46 -r edf2bfcd8d97 InteractiveCharts/LineChart/Helpers/LinePath.swift --- a/InteractiveCharts/LineChart/Helpers/LinePath.swift Wed Apr 28 18:58:15 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -// -// LinePath.swift -// InteractiveCharts -// -// Created by Dennis Concepción Martín on 26/4/21. -// - -import SwiftUI - -struct LinePath: Shape { - var data: [Double] - var (width, height): (CGFloat, CGFloat) - @Binding var pathPoints: [CGPoint] - - func path(in rect: CGRect) -> Path { - var path = Path() - - let normalizedData = normalize(data) - let widthBetweenDataPoints = Double(width) / Double(normalizedData.count - 1) // Remove first point - let initialPoint = normalizedData[0] * Double(height) - var x: Double = 0 - - path.move(to: CGPoint(x: x, y: initialPoint)) - for y in normalizedData { - if normalizedData.firstIndex(of: y) != 0 { // Skip first point - x += widthBetweenDataPoints - let y = y * Double(height) - path.addLine(to: CGPoint(x: x, y: y)) - } - - // Append current point to an array. Later will be used for Drag Gesture - pathPoints.append(path.currentPoint!) - } - - return path - } - - /* - Get data -> normalize it -> 0 <= output <= 1 - */ - func normalize(_ data: [Double]) -> [Double] { - var normalData = [Double]() - let min = data.min()! - let max = data.max()! - - for value in data { - let normal = (value - min) / (max - min) - normalData.append(normal) - } - - return normalData - } -} - diff -r fdab6510dc46 -r edf2bfcd8d97 InteractiveCharts/LineChart/Helpers/LineView.swift --- a/InteractiveCharts/LineChart/Helpers/LineView.swift Wed Apr 28 18:58:15 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -// -// LineView.swift -// InteractiveCharts -// -// Created by Dennis Concepción Martín on 26/4/21. -// - -import SwiftUI - -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 { - 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. - */ - private 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 - */ - 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) - } -} diff -r fdab6510dc46 -r edf2bfcd8d97 InteractiveCharts/UI Previews/ChartViewPreview.swift --- a/InteractiveCharts/UI Previews/ChartViewPreview.swift Wed Apr 28 18:58:15 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -// -// ChartViewPreview.swift -// InteractiveCharts -// -// Created by Dennis Concepción Martín on 26/4/21. -// - -import SwiftUI - -struct ChartViewPreview: View { - var data: [Double] - var dates: [String]? - var hours: [String]? - - var range = ["5D", "1M", "3M", "1Y", "5Y"] - @State private var selectedRange = "3M" - - var body: some View { - NavigationView { - VStack(alignment: .leading) { - Text("Apple Inc") - .font(.title3) - .padding([.horizontal, .bottom]) - - Picker("Select a range", selection: $selectedRange) { - ForEach(range, id: \.self) { - Text($0) - } - } - .pickerStyle(SegmentedPickerStyle()) - .padding(.horizontal) - - ChartView(data: data, dates: dates, hours: hours) - .padding(.vertical) - } - .navigationTitle("AAPL") - .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { - Button(action: {}) { - Image(systemName: "star") - } - } - - ToolbarItem(placement: .navigationBarLeading) { - Button(action: {}) { - Image(systemName: "plus.circle") - } - } - } - } - .navigationViewStyle(StackNavigationViewStyle()) - } -} diff -r fdab6510dc46 -r edf2bfcd8d97 InteractiveCharts/UI Previews/ContentView.swift --- a/InteractiveCharts/UI Previews/ContentView.swift Wed Apr 28 18:58:15 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -// -// ContentView.swift -// InteractiveCharts -// -// Created by Dennis Concepción Martín on 26/4/21. -// - -import SwiftUI - -struct ContentView: View { - var body: some View { - TabView { - ChartViewPreview(data: generateSampleData(350)) - .tabItem { - Label("ChartView", systemImage: "house") - } - } - } -} - -struct ContentView_Previews: PreviewProvider { - static var previews: some View { - ContentView() - } -} diff -r fdab6510dc46 -r edf2bfcd8d97 InteractiveCharts/UI Previews/Sample data/GenerateSampleData.swift --- a/InteractiveCharts/UI Previews/Sample data/GenerateSampleData.swift Wed Apr 28 18:58:15 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ -// -// GenerateSampleData.swift -// InteractiveCharts -// -// Created by Dennis Concepción Martín on 26/4/21. -// - -import SwiftUI - -/* - Generate sample data - */ -func generateSampleData(_ n: Int) -> [Double] { - var prices = [Double]() - - for _ in (1.. - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff -r fdab6510dc46 -r edf2bfcd8d97 InteractiveChartsTests/InteractiveChartsTests.swift --- a/InteractiveChartsTests/InteractiveChartsTests.swift Wed Apr 28 18:58:15 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -// -// InteractiveChartsTests.swift -// InteractiveChartsTests -// -// Created by Dennis Concepción Martín on 26/4/21. -// - -import XCTest -@testable import InteractiveCharts - -class InteractiveChartsTests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testPerformanceExample() throws { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. - } - } - -} diff -r fdab6510dc46 -r edf2bfcd8d97 Sources/InteractiveCharts/Info.plist --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sources/InteractiveCharts/Info.plist Wed Apr 28 19:01:40 2021 +0200 @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff -r fdab6510dc46 -r edf2bfcd8d97 Sources/InteractiveCharts/InteractiveCharts.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sources/InteractiveCharts/InteractiveCharts.h Wed Apr 28 19:01:40 2021 +0200 @@ -0,0 +1,18 @@ +// +// InteractiveCharts.h +// InteractiveCharts +// +// Created by Dennis Concepción Martín on 26/4/21. +// + +#import + +//! Project version number for InteractiveCharts. +FOUNDATION_EXPORT double InteractiveChartsVersionNumber; + +//! Project version string for InteractiveCharts. +FOUNDATION_EXPORT const unsigned char InteractiveChartsVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff -r fdab6510dc46 -r edf2bfcd8d97 Sources/InteractiveCharts/LineChart/ChartView.swift --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sources/InteractiveCharts/LineChart/ChartView.swift Wed Apr 28 19:01:40 2021 +0200 @@ -0,0 +1,27 @@ +// +// ChartView.swift +// InteractiveCharts +// +// Created by Dennis Concepción Martín on 26/4/21. +// + +import SwiftUI + +struct ChartView: View { + var data: [Double] + var dates: [String]? + var hours: [String]? + + @State private var showingIndicators = false + @State private var indexPosition = Int() + + var body: some View { + VStack { + ChartLabel(data: data, dates: dates, hours: hours, indexPosition: $indexPosition) + .opacity(showingIndicators ? 1: 0) + .padding(.vertical) + + LineView(data: data, showingIndicators: $showingIndicators, indexPosition: $indexPosition) + } + } +} diff -r fdab6510dc46 -r edf2bfcd8d97 Sources/InteractiveCharts/LineChart/Helpers/ChartLabel.swift --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sources/InteractiveCharts/LineChart/Helpers/ChartLabel.swift Wed Apr 28 19:01:40 2021 +0200 @@ -0,0 +1,50 @@ +// +// ChartLabel.swift +// InteractiveCharts +// +// Created by Dennis Concepción Martín on 26/4/21. +// + +import SwiftUI + +struct ChartLabel: View { + var data: [Double] + var dates: [String]? + var hours: [String]? + + @Binding var indexPosition: Int // Data point position + + var body: some View { + HStack { + Group { + if let dates = self.dates { + let date = formatStringDate(dates[indexPosition]) + Text(date) + } + if let hours = self.hours { + let hour = hours[indexPosition] + Text(hour) + } + Text("\(data[indexPosition], specifier: "%.2f")") + .foregroundColor(Color(.systemBlue)) + } + .font(.headline) + } + } + + /* + Take string in format yy-MM-dd (2021-01-01) and transform it + to long default string format + */ + private func formatStringDate(_ stringDate: String) -> String { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yy-MM-dd" + let date = dateFormatter.date(from: stringDate) + + // Format date to the final format + dateFormatter.dateStyle = .long + let finalDate = dateFormatter.string(from: date!) + + return finalDate + } +} diff -r fdab6510dc46 -r edf2bfcd8d97 Sources/InteractiveCharts/LineChart/Helpers/IndicatorPoint.swift --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sources/InteractiveCharts/LineChart/Helpers/IndicatorPoint.swift Wed Apr 28 19:01:40 2021 +0200 @@ -0,0 +1,23 @@ +// +// IndicatorPoint.swift +// InteractiveCharts +// +// Created by Dennis Concepción Martín on 26/4/21. +// + +import SwiftUI + +struct IndicatorPoint: View { + var body: some View { + Circle() + .frame(width: 20, height: 20) + .foregroundColor(Color(.systemBlue)) + + } +} + +struct IndicatorPoint_Previews: PreviewProvider { + static var previews: some View { + IndicatorPoint() + } +} diff -r fdab6510dc46 -r edf2bfcd8d97 Sources/InteractiveCharts/LineChart/Helpers/LinePath.swift --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sources/InteractiveCharts/LineChart/Helpers/LinePath.swift Wed Apr 28 19:01:40 2021 +0200 @@ -0,0 +1,54 @@ +// +// LinePath.swift +// InteractiveCharts +// +// Created by Dennis Concepción Martín on 26/4/21. +// + +import SwiftUI + +struct LinePath: Shape { + var data: [Double] + var (width, height): (CGFloat, CGFloat) + @Binding var pathPoints: [CGPoint] + + func path(in rect: CGRect) -> Path { + var path = Path() + + let normalizedData = normalize(data) + let widthBetweenDataPoints = Double(width) / Double(normalizedData.count - 1) // Remove first point + let initialPoint = normalizedData[0] * Double(height) + var x: Double = 0 + + path.move(to: CGPoint(x: x, y: initialPoint)) + for y in normalizedData { + if normalizedData.firstIndex(of: y) != 0 { // Skip first point + x += widthBetweenDataPoints + let y = y * Double(height) + path.addLine(to: CGPoint(x: x, y: y)) + } + + // Append current point to an array. Later will be used for Drag Gesture + pathPoints.append(path.currentPoint!) + } + + return path + } + + /* + Get data -> normalize it -> 0 <= output <= 1 + */ + func normalize(_ data: [Double]) -> [Double] { + var normalData = [Double]() + let min = data.min()! + let max = data.max()! + + for value in data { + let normal = (value - min) / (max - min) + normalData.append(normal) + } + + return normalData + } +} + diff -r fdab6510dc46 -r edf2bfcd8d97 Sources/InteractiveCharts/LineChart/Helpers/LineView.swift --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sources/InteractiveCharts/LineChart/Helpers/LineView.swift Wed Apr 28 19:01:40 2021 +0200 @@ -0,0 +1,103 @@ +// +// LineView.swift +// InteractiveCharts +// +// Created by Dennis Concepción Martín on 26/4/21. +// + +import SwiftUI + +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 { + 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. + */ + private 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 + */ + 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) + } +} diff -r fdab6510dc46 -r edf2bfcd8d97 Sources/InteractiveCharts/UI Previews/ChartViewPreview.swift --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sources/InteractiveCharts/UI Previews/ChartViewPreview.swift Wed Apr 28 19:01:40 2021 +0200 @@ -0,0 +1,53 @@ +// +// ChartViewPreview.swift +// InteractiveCharts +// +// Created by Dennis Concepción Martín on 26/4/21. +// + +import SwiftUI + +struct ChartViewPreview: View { + var data: [Double] + var dates: [String]? + var hours: [String]? + + var range = ["5D", "1M", "3M", "1Y", "5Y"] + @State private var selectedRange = "3M" + + var body: some View { + NavigationView { + VStack(alignment: .leading) { + Text("Apple Inc") + .font(.title3) + .padding([.horizontal, .bottom]) + + Picker("Select a range", selection: $selectedRange) { + ForEach(range, id: \.self) { + Text($0) + } + } + .pickerStyle(SegmentedPickerStyle()) + .padding(.horizontal) + + ChartView(data: data, dates: dates, hours: hours) + .padding(.vertical) + } + .navigationTitle("AAPL") + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button(action: {}) { + Image(systemName: "star") + } + } + + ToolbarItem(placement: .navigationBarLeading) { + Button(action: {}) { + Image(systemName: "plus.circle") + } + } + } + } + .navigationViewStyle(StackNavigationViewStyle()) + } +} diff -r fdab6510dc46 -r edf2bfcd8d97 Sources/InteractiveCharts/UI Previews/ContentView.swift --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sources/InteractiveCharts/UI Previews/ContentView.swift Wed Apr 28 19:01:40 2021 +0200 @@ -0,0 +1,25 @@ +// +// ContentView.swift +// InteractiveCharts +// +// Created by Dennis Concepción Martín on 26/4/21. +// + +import SwiftUI + +struct ContentView: View { + var body: some View { + TabView { + ChartViewPreview(data: generateSampleData(350)) + .tabItem { + Label("ChartView", systemImage: "house") + } + } + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} diff -r fdab6510dc46 -r edf2bfcd8d97 Sources/InteractiveCharts/UI Previews/Sample data/GenerateSampleData.swift --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sources/InteractiveCharts/UI Previews/Sample data/GenerateSampleData.swift Wed Apr 28 19:01:40 2021 +0200 @@ -0,0 +1,29 @@ +// +// GenerateSampleData.swift +// InteractiveCharts +// +// Created by Dennis Concepción Martín on 26/4/21. +// + +import SwiftUI + +/* + Generate sample data + */ +func generateSampleData(_ n: Int) -> [Double] { + var prices = [Double]() + + for _ in (1.. + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff -r fdab6510dc46 -r edf2bfcd8d97 Sources/InteractiveChartsTests/InteractiveChartsTests.swift --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sources/InteractiveChartsTests/InteractiveChartsTests.swift Wed Apr 28 19:01:40 2021 +0200 @@ -0,0 +1,33 @@ +// +// InteractiveChartsTests.swift +// InteractiveChartsTests +// +// Created by Dennis Concepción Martín on 26/4/21. +// + +import XCTest +@testable import InteractiveCharts + +class InteractiveChartsTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +}