Mercurial > public > stock-charts
changeset 5:f828c7c408d4
Add source code
author | Dennis Concepción Martín <66180929+denniscm190@users.noreply.github.com> |
---|---|
date | Mon, 26 Apr 2021 19:02:46 +0200 |
parents | fd3fb56afc53 |
children | 9802bdd5c7f3 |
files | InteractiveCharts.xcodeproj/project.pbxproj InteractiveCharts.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate InteractiveCharts/LineChart/ChartView.swift InteractiveCharts/LineChart/Helpers/ChartLabel.swift InteractiveCharts/LineChart/Helpers/IndicatorPoint.swift InteractiveCharts/LineChart/Helpers/LinePath.swift InteractiveCharts/LineChart/Helpers/LineView.swift |
diffstat | 7 files changed, 254 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/InteractiveCharts.xcodeproj/project.pbxproj Mon Apr 26 11:14:50 2021 +0200 +++ b/InteractiveCharts.xcodeproj/project.pbxproj Mon Apr 26 19:02:46 2021 +0200 @@ -7,6 +7,11 @@ objects = { /* Begin PBXBuildFile section */ + 95075B3F26370E81005E0066 /* LineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95075B3E26370E81005E0066 /* LineView.swift */; }; + 95075B4326370EAA005E0066 /* LinePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95075B4226370EAA005E0066 /* LinePath.swift */; }; + 95075B472637153E005E0066 /* ChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95075B462637153E005E0066 /* ChartView.swift */; }; + 95075B4B263718C7005E0066 /* IndicatorPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95075B4A263718C7005E0066 /* IndicatorPoint.swift */; }; + 95075B4F2637227D005E0066 /* ChartLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95075B4E2637227D005E0066 /* ChartLabel.swift */; }; 955788432636B8D800D1192D /* InteractiveCharts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 955788392636B8D800D1192D /* InteractiveCharts.framework */; }; 955788482636B8D800D1192D /* InteractiveChartsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955788472636B8D800D1192D /* InteractiveChartsTests.swift */; }; 9557884A2636B8D800D1192D /* InteractiveCharts.h in Headers */ = {isa = PBXBuildFile; fileRef = 9557883C2636B8D800D1192D /* InteractiveCharts.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -23,6 +28,11 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 95075B3E26370E81005E0066 /* LineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineView.swift; sourceTree = "<group>"; }; + 95075B4226370EAA005E0066 /* LinePath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinePath.swift; sourceTree = "<group>"; }; + 95075B462637153E005E0066 /* ChartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartView.swift; sourceTree = "<group>"; }; + 95075B4A263718C7005E0066 /* IndicatorPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndicatorPoint.swift; sourceTree = "<group>"; }; + 95075B4E2637227D005E0066 /* ChartLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartLabel.swift; sourceTree = "<group>"; }; 955788392636B8D800D1192D /* InteractiveCharts.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = InteractiveCharts.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9557883C2636B8D800D1192D /* InteractiveCharts.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InteractiveCharts.h; sourceTree = "<group>"; }; 9557883D2636B8D800D1192D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; @@ -50,6 +60,26 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 95075B5426372506005E0066 /* LineChart */ = { + isa = PBXGroup; + children = ( + 95075B462637153E005E0066 /* ChartView.swift */, + 95075B552637251A005E0066 /* Helpers */, + ); + path = LineChart; + sourceTree = "<group>"; + }; + 95075B552637251A005E0066 /* Helpers */ = { + isa = PBXGroup; + children = ( + 95075B3E26370E81005E0066 /* LineView.swift */, + 95075B4226370EAA005E0066 /* LinePath.swift */, + 95075B4A263718C7005E0066 /* IndicatorPoint.swift */, + 95075B4E2637227D005E0066 /* ChartLabel.swift */, + ); + path = Helpers; + sourceTree = "<group>"; + }; 9557882F2636B8D700D1192D = { isa = PBXGroup; children = ( @@ -73,6 +103,7 @@ children = ( 9557883C2636B8D800D1192D /* InteractiveCharts.h */, 9557883D2636B8D800D1192D /* Info.plist */, + 95075B5426372506005E0066 /* LineChart */, ); path = InteractiveCharts; sourceTree = "<group>"; @@ -147,6 +178,7 @@ TargetAttributes = { 955788382636B8D800D1192D = { CreatedOnToolsVersion = 12.4; + LastSwiftMigration = 1240; }; 955788412636B8D800D1192D = { CreatedOnToolsVersion = 12.4; @@ -194,6 +226,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 95075B4326370EAA005E0066 /* LinePath.swift in Sources */, + 95075B3F26370E81005E0066 /* LineView.swift in Sources */, + 95075B4B263718C7005E0066 /* IndicatorPoint.swift in Sources */, + 95075B4F2637227D005E0066 /* ChartLabel.swift in Sources */, + 95075B472637153E005E0066 /* ChartView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -341,6 +378,7 @@ 9557884E2636B8D800D1192D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = MTX83R5H8X; @@ -358,6 +396,7 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -366,6 +405,7 @@ 9557884F2636B8D800D1192D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = MTX83R5H8X; @@ -391,6 +431,7 @@ 955788512636B8D800D1192D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = MTX83R5H8X; INFOPLIST_FILE = InteractiveChartsTests/Info.plist; @@ -409,6 +450,7 @@ 955788522636B8D800D1192D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = MTX83R5H8X; INFOPLIST_FILE = InteractiveChartsTests/Info.plist;
Binary file InteractiveCharts.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/InteractiveCharts/LineChart/ChartView.swift Mon Apr 26 19:02:46 2021 +0200 @@ -0,0 +1,42 @@ +// +// ChartView.swift +// InteractiveCharts +// +// Created by Dennis Concepción Martín on 26/4/21. +// + +import SwiftUI +import GameplayKit + +struct ChartView: View { + var data: [Double] + var dates: [String]? + var hours: [String]? + + @State private var showingLabel = false + @State private var IndicatorPointPosition: CGPoint = .zero + @State private var indexPosition = Int() + + var body: some View { + ZStack { + if showingLabel { + ChartLabel(data: data, dates: dates, hours: hours, indexPosition: $indexPosition) + } + + LineView(data: data) + } + } +} + +struct ChartView_Previews: PreviewProvider { + static var previews: some View { + ChartView(data: [10.0, 11.1, 10.5, 11.0, 11.9, 11.7, 10.4, 10.9]) + } + + /* + Generate sample data + */ + static func generateSampleData(_ n: Int) -> [Double] { + return (0..<n).map { _ in .random(in: 1...20) } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/InteractiveCharts/LineChart/Helpers/ChartLabel.swift Mon Apr 26 19:02:46 2021 +0200 @@ -0,0 +1,56 @@ +// +// 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(colour) + } + .font(.subheadline) + } + } + + /* + 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 + } +} + +struct Label_Previews: PreviewProvider { + static var previews: some View { + ChartLabel(data: [10.0, 11.1, 10.5, 10.0, 11.9, 11.7, 10.4, 10.9], indexPosition: .constant(0)) + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/InteractiveCharts/LineChart/Helpers/IndicatorPoint.swift Mon Apr 26 19:02:46 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() + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/InteractiveCharts/LineChart/Helpers/LinePath.swift Mon Apr 26 19:02:46 2021 +0200 @@ -0,0 +1,55 @@ +// +// 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) + + 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 + + var pathPoints = [CGPoint]() + + 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 + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/InteractiveCharts/LineChart/Helpers/LineView.swift Mon Apr 26 19:02:46 2021 +0200 @@ -0,0 +1,36 @@ +// +// LineView.swift +// InteractiveCharts +// +// Created by Dennis Concepción Martín on 26/4/21. +// + +import SwiftUI + +struct LineView: View { + var data: [Double] + + 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)) + } + } + + /* + 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) + } + + return color + } +}