HikeGraph.swift


  1. import SwiftUI
  2. extension Animation {
  3. static func ripple() -> Animation {
  4. Animation.default
  5. }
  6. }
  7. struct HikeGraph: View {
  8. var hike: Hike
  9. var path: KeyPath<Hike.Observation, Range<Double>>
  10. var color: Color {
  11. switch path {
  12. case \.elevation:
  13. return .gray
  14. case \.heartRate:
  15. return Color(hue: 0, saturation: 0.5, brightness: 0.7)
  16. case \.pace:
  17. return Color(hue: 0.7, saturation: 0.4, brightness: 0.7)
  18. default:
  19. return .black
  20. }
  21. }
  22. var body: some View {
  23. let data = hike.observations
  24. let overallRange = rangeOfRanges(data.lazy.map { $0[keyPath: path] })
  25. let maxMagnitude = data.map { magnitude(of: $0[keyPath: path]) }.max()!
  26. let heightRatio = 1 - CGFloat(maxMagnitude / magnitude(of: overallRange))
  27. return GeometryReader { proxy in
  28. HStack(alignment: .bottom, spacing: proxy.size.width / 120) {
  29. ForEach(Array(data.enumerated()), id: \.offset) { index, observation in
  30. GraphCapsule(
  31. index: index,
  32. height: proxy.size.height,
  33. range: observation[keyPath: path],
  34. overallRange: overallRange)
  35. .colorMultiply(color)
  36. .transition(.slide)
  37. .animation(.ripple())
  38. }
  39. .offset(x: 0, y: proxy.size.height * heightRatio)
  40. }
  41. }
  42. }
  43. }