# HG changeset patch # User Dennis Concepción Martín <66180929+denniscm190@users.noreply.github.com> # Date 1615996873 -3600 # Node ID 39428219f83234834fd498e4cf6b2005ab1faa82 # Parent 9e23e9b0ab3609786e39aabb53303c6ec34230fb Implementing LineChart drag animation diff -r 9e23e9b0ab36 -r 39428219f832 LazyBear.xcodeproj/project.pbxproj --- a/LazyBear.xcodeproj/project.pbxproj Mon Mar 15 20:06:24 2021 +0100 +++ b/LazyBear.xcodeproj/project.pbxproj Wed Mar 17 17:01:13 2021 +0100 @@ -49,7 +49,7 @@ 95ACB5A925E0397B00A3CCC8 /* CompanyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95ACB5A825E0397B00A3CCC8 /* CompanyView.swift */; }; 95ACB5AC25E03A7D00A3CCC8 /* themes.json in Resources */ = {isa = PBXBuildFile; fileRef = 95ACB5AB25E03A7D00A3CCC8 /* themes.json */; }; 95ACB5AF25E03AA100A3CCC8 /* ThemeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95ACB5AE25E03AA100A3CCC8 /* ThemeModel.swift */; }; - 95AEF3AC25FFBB4D001B77BB /* LinePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95AEF3AB25FFBB4D001B77BB /* LinePath.swift */; }; + 95AEF3AC25FFBB4D001B77BB /* LineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95AEF3AB25FFBB4D001B77BB /* LineView.swift */; }; 95AEF3B025FFD8CF001B77BB /* LineChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95AEF3AF25FFD8CF001B77BB /* LineChart.swift */; }; 95AEF3B325FFDC04001B77BB /* DeviceSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95AEF3B225FFDC04001B77BB /* DeviceSize.swift */; }; 95B3E09F25E127D7007EFDE3 /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95B3E09E25E127D7007EFDE3 /* Request.swift */; }; @@ -72,6 +72,7 @@ 95F0460B25E970DB006A5A17 /* LanguagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95F0460A25E970DB006A5A17 /* LanguagePicker.swift */; }; 95F0461025E976B5006A5A17 /* SettingRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95F0460F25E976B5006A5A17 /* SettingRow.swift */; }; 95F0462825E98376006A5A17 /* default.png in Resources */ = {isa = PBXBuildFile; fileRef = 95F0462625E98376006A5A17 /* default.png */; }; + 95F8433E2602455E00770F8A /* IndicatorPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95F8433D2602455E00770F8A /* IndicatorPoint.swift */; }; 95F90AB825F280190023A4B0 /* InsiderTransactions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95F90AB725F280190023A4B0 /* InsiderTransactions.swift */; }; /* End PBXBuildFile section */ @@ -122,7 +123,7 @@ 95ACB5A825E0397B00A3CCC8 /* CompanyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompanyView.swift; sourceTree = ""; }; 95ACB5AB25E03A7D00A3CCC8 /* themes.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = themes.json; sourceTree = ""; }; 95ACB5AE25E03AA100A3CCC8 /* ThemeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeModel.swift; sourceTree = ""; }; - 95AEF3AB25FFBB4D001B77BB /* LinePath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinePath.swift; sourceTree = ""; }; + 95AEF3AB25FFBB4D001B77BB /* LineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineView.swift; sourceTree = ""; }; 95AEF3AF25FFD8CF001B77BB /* LineChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineChart.swift; sourceTree = ""; }; 95AEF3B225FFDC04001B77BB /* DeviceSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceSize.swift; sourceTree = ""; }; 95B3E09E25E127D7007EFDE3 /* Request.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = ""; }; @@ -143,6 +144,7 @@ 95F0460A25E970DB006A5A17 /* LanguagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguagePicker.swift; sourceTree = ""; }; 95F0460F25E976B5006A5A17 /* SettingRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingRow.swift; sourceTree = ""; }; 95F0462625E98376006A5A17 /* default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = default.png; sourceTree = ""; }; + 95F8433D2602455E00770F8A /* IndicatorPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndicatorPoint.swift; sourceTree = ""; }; 95F90AB725F280190023A4B0 /* InsiderTransactions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsiderTransactions.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -282,8 +284,9 @@ 9520C26E25F4D43D0070DD71 /* TransactionDetail.swift */, 95DED9DA25F2B268000DFCBA /* InsiderSummary.swift */, 95E9D09625F6AA0400A947A1 /* ActionView.swift */, - 95AEF3AB25FFBB4D001B77BB /* LinePath.swift */, 95AEF3AF25FFD8CF001B77BB /* LineChart.swift */, + 95AEF3AB25FFBB4D001B77BB /* LineView.swift */, + 95F8433D2602455E00770F8A /* IndicatorPoint.swift */, ); path = UI; sourceTree = ""; @@ -406,7 +409,7 @@ 95F0461025E976B5006A5A17 /* SettingRow.swift in Sources */, 95F0460825E9704F006A5A17 /* ThemePicker.swift in Sources */, 95A5D95A25FCEDDB0090C1EA /* CompanyOption.swift in Sources */, - 95AEF3AC25FFBB4D001B77BB /* LinePath.swift in Sources */, + 95AEF3AC25FFBB4D001B77BB /* LineView.swift in Sources */, 95672B9825DDA54700DCBE4A /* Persistence.swift in Sources */, 958A735B25E0264E00FD7ECA /* CompanyModel.swift in Sources */, 95B3E09F25E127D7007EFDE3 /* Request.swift in Sources */, @@ -421,6 +424,7 @@ 95A5D95D25FCEEBC0090C1EA /* GetUrl.swift in Sources */, 9517626325EEBD3800733235 /* IexAttribution.swift in Sources */, 95F90AB825F280190023A4B0 /* InsiderTransactions.swift in Sources */, + 95F8433E2602455E00770F8A /* IndicatorPoint.swift in Sources */, 95ACB5AF25E03AA100A3CCC8 /* ThemeModel.swift in Sources */, 95672B8F25DDA54700DCBE4A /* LazyBearApp.swift in Sources */, 9517626025EEB37E00733235 /* PriceModel.swift in Sources */, diff -r 9e23e9b0ab36 -r 39428219f832 LazyBear.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate Binary file LazyBear.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate has changed diff -r 9e23e9b0ab36 -r 39428219f832 LazyBear/UI/ChartView.swift --- a/LazyBear/UI/ChartView.swift Mon Mar 15 20:06:24 2021 +0100 +++ b/LazyBear/UI/ChartView.swift Wed Mar 17 17:01:13 2021 +0100 @@ -26,6 +26,7 @@ let prices = historicalPrices.map { $0.close } LineChart(data: prices) + .padding(.vertical) } .onAppear { let url = getUrl(endpoint: .historicalPrices, symbol: symbol, range: "3m") diff -r 9e23e9b0ab36 -r 39428219f832 LazyBear/UI/IndicatorPoint.swift --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/UI/IndicatorPoint.swift Wed Mar 17 17:01:13 2021 +0100 @@ -0,0 +1,21 @@ +// +// IndicatorPoint.swift +// LazyBear +// +// Created by Dennis Concepción Martín on 17/3/21. +// + +import SwiftUI + +struct IndicatorPoint: View { + var body: some View { + Circle() + .frame(width: 10, height: 10) + } +} + +struct IndicatorPoint_Previews: PreviewProvider { + static var previews: some View { + IndicatorPoint() + } +} diff -r 9e23e9b0ab36 -r 39428219f832 LazyBear/UI/LineChart.swift --- a/LazyBear/UI/LineChart.swift Mon Mar 15 20:06:24 2021 +0100 +++ b/LazyBear/UI/LineChart.swift Wed Mar 17 17:01:13 2021 +0100 @@ -12,17 +12,27 @@ @EnvironmentObject var deviceSize: DeviceSize var body: some View { + if !data.isEmpty { + let normalizedData = normalize(data) + VStack { - ZStack { - Grid(height: deviceSize.width, width: deviceSize.width) - .clipped() - .background(LinePath(width: deviceSize.width, data: data)) - - - } + LineView(width: deviceSize.width, height: deviceSize.width / 3, normalizedData: normalizedData) } - .frame(width: deviceSize.width, height: deviceSize.width / 2) - + .frame(width: deviceSize.width, height: deviceSize.width / 3) + } + } + + 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 } } @@ -32,25 +42,3 @@ .environmentObject(DeviceSize()) } } - -struct Grid: View { - var height: CGFloat - var width: CGFloat - - var body: some View { - VStack { - Rectangle() - .stroke(Color.gray.opacity(0.2), lineWidth: 0.5) - - Group { - Rectangle() - .stroke(Color.gray.opacity(0.2), lineWidth: 0.5) - - Rectangle() - .stroke(Color.gray.opacity(0.2), lineWidth: 0.5) - } - .offset(y: -8) - .padding(.bottom, -8) - } - } -} diff -r 9e23e9b0ab36 -r 39428219f832 LazyBear/UI/LinePath.swift --- a/LazyBear/UI/LinePath.swift Mon Mar 15 20:06:24 2021 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -// -// LinePath.swift -// LazyBear -// -// Created by Dennis Concepción Martín on 15/3/21. -// - -import SwiftUI - -struct LinePath: View { - var width: CGFloat - var data: [Double] - - var body: some View { - let height = width/2 - if !data.isEmpty { - let normalizedData = normalize(data) - - // Substract 2 to skip the first and the last item - let widthBetweenPoints = Double(width) / Double(normalizedData.count - 2) - - Path { path in - let initialPoint = normalizedData[0] * Double(height) - var x: Double = 0 - path.move(to: CGPoint(x: x, y: initialPoint)) - - for y in normalizedData { - // Skip first item - if normalizedData.firstIndex(of: y) != 0 { - x += widthBetweenPoints - let y = y * Double(height) - path.addLine(to: CGPoint(x: x, y: y)) - } - } - } - .stroke(Color.green, lineWidth: 2) - .rotationEffect(.degrees(180), anchor: .center) // The path must be rotated - .rotation3DEffect(.degrees(180), axis: (x: 0.0, y: 1.0, z: 0.0)) - - } - } - - 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 - } - -} - -struct LinePath_Previews: PreviewProvider { - static var previews: some View { - GeometryReader { geo in - VStack { - let data: [Double] = [50.0, 50.5, 51.0, 50.4, 50.8, 51.3, 51.5, 52, 51.9, 52.4] - LinePath(width: geo.size.width, data: data) - } - } - } -} diff -r 9e23e9b0ab36 -r 39428219f832 LazyBear/UI/LineView.swift --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/UI/LineView.swift Wed Mar 17 17:01:13 2021 +0100 @@ -0,0 +1,66 @@ +// +// LineView.swift +// LazyBear +// +// Created by Dennis Concepción Martín on 15/3/21. +// + +import SwiftUI + +struct LineView: View { + var width: CGFloat + var height: CGFloat + var normalizedData: [Double] + + // Drag gesture + @State private var touchLocation: CGPoint = .zero + + var body: some View { + Line(width: width, height: height, normalizedData: normalizedData) + .stroke(Color.green, lineWidth: 2) + .rotationEffect(.degrees(180), anchor: .center) // The path must be rotated + .rotation3DEffect(.degrees(180), axis: (x: 0.0, y: 1.0, z: 0.0)) + .gesture(DragGesture() // Add gesture + .onChanged({ value in // Take value of the gesture + print("Location - > \(value.location)") + }) + ) + } +} + +struct Line: Shape { + var width: CGFloat + var height: CGFloat + var normalizedData: [Double] + + func path(in rect: CGRect) -> Path { + var path = Path() + + // Substract 2 to skip the first and the last item + let widthBetweenPoints = Double(width) / Double(normalizedData.count - 2) + let initialPoint = normalizedData[0] * Double(height) + var x: Double = 0 + + path.move(to: CGPoint(x: x, y: initialPoint)) + for y in normalizedData { + // Skip first item + if normalizedData.firstIndex(of: y) != 0 { + x += widthBetweenPoints + let y = y * Double(height) + path.addLine(to: CGPoint(x: x, y: y)) + } + } + return path + } +} + +struct LineView_Previews: PreviewProvider { + static var previews: some View { + GeometryReader { geo in + VStack { + let normalizedData: [Double] = [0, 0.1, 0.15, 0.2, 0.3, 0.4, 0.35, 0.38, 0.5, 0.55, 0.6, 0.57, 0.8, 1] + LineView(width: geo.size.width, height: geo.size.width / 2, normalizedData: normalizedData) + } + } + } +}