IOBChart.swift 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. //
  2. // IOBChart.swift
  3. // LoopUI
  4. //
  5. // Copyright © 2019 LoopKit Authors. All rights reserved.
  6. //
  7. import Foundation
  8. import LoopKit
  9. import SwiftCharts
  10. import HealthKit
  11. import UIKit
  12. public class IOBChart: ChartProviding {
  13. static let chartUnit = HKUnit.internationalUnit()
  14. public init() {
  15. }
  16. /// The chart points for IOB
  17. public private(set) var iobPoints: [ChartPoint] = [] {
  18. didSet {
  19. if let lastDate = iobPoints.last?.x as? ChartAxisValueDate {
  20. endDate = lastDate.date
  21. }
  22. }
  23. }
  24. /// The minimum range to display for insulin values.
  25. private let iobDisplayRangePoints: [ChartPoint] = [0, 1].map {
  26. return ChartPoint(
  27. x: ChartAxisValue(scalar: 0),
  28. y: ChartAxisValueInt($0)
  29. )
  30. }
  31. public private(set) var endDate: Date?
  32. private var iobChartCache: ChartPointsTouchHighlightLayerViewCache?
  33. }
  34. public extension IOBChart {
  35. func didReceiveMemoryWarning() {
  36. iobPoints = []
  37. iobChartCache = nil
  38. }
  39. func generate(withFrame frame: CGRect, xAxisModel: ChartAxisModel, xAxisValues: [ChartAxisValue], axisLabelSettings: ChartLabelSettings, guideLinesLayerSettings: ChartGuideLinesLayerSettings, colors: ChartColorPalette, chartSettings: ChartSettings, labelsWidthY: CGFloat, gestureRecognizer: UIGestureRecognizer?, traitCollection: UITraitCollection) -> Chart
  40. {
  41. let yAxisValues = ChartAxisValuesStaticGenerator.generateYAxisValuesWithChartPoints(iobPoints + iobDisplayRangePoints, minSegmentCount: 2, maxSegmentCount: 3, multiple: 0.5, axisValueGenerator: { ChartAxisValueDouble($0, labelSettings: axisLabelSettings) }, addPaddingSegmentIfEdge: false)
  42. let yAxisModel = ChartAxisModel(axisValues: yAxisValues, lineColor: colors.axisLine, labelSpaceReservationMode: .fixed(labelsWidthY))
  43. let coordsSpace = ChartCoordsSpaceLeftBottomSingleAxis(chartSettings: chartSettings, chartFrame: frame, xModel: xAxisModel, yModel: yAxisModel)
  44. let (xAxisLayer, yAxisLayer, innerFrame) = (coordsSpace.xAxisLayer, coordsSpace.yAxisLayer, coordsSpace.chartInnerFrame)
  45. // The IOB area
  46. let lineModel = ChartLineModel(chartPoints: iobPoints, lineColor: colors.insulinTint, lineWidth: 2, animDuration: 0, animDelay: 0)
  47. let iobLine = ChartPointsLineLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, lineModels: [lineModel])
  48. let iobArea = ChartPointsFillsLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, fills: [ChartPointsFill(chartPoints: iobPoints, fillColor: colors.insulinTint.withAlphaComponent(0.5))])
  49. // Grid lines
  50. let gridLayer = ChartGuideLinesForValuesLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, settings: guideLinesLayerSettings, axisValuesX: Array(xAxisValues.dropFirst().dropLast()), axisValuesY: yAxisValues)
  51. // 0-line
  52. let dummyZeroChartPoint = ChartPoint(x: ChartAxisValueDouble(0), y: ChartAxisValueDouble(0))
  53. let zeroGuidelineLayer = ChartPointsViewsLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, chartPoints: [dummyZeroChartPoint], viewGenerator: {(chartPointModel, layer, chart) -> UIView? in
  54. let width: CGFloat = 0.5
  55. let viewFrame = CGRect(x: chart.contentView.bounds.minX, y: chartPointModel.screenLoc.y - width / 2, width: chart.contentView.bounds.size.width, height: width)
  56. let v = UIView(frame: viewFrame)
  57. v.layer.backgroundColor = colors.insulinTint.cgColor
  58. return v
  59. })
  60. if gestureRecognizer != nil {
  61. iobChartCache = ChartPointsTouchHighlightLayerViewCache(
  62. xAxisLayer: xAxisLayer,
  63. yAxisLayer: yAxisLayer,
  64. axisLabelSettings: axisLabelSettings,
  65. chartPoints: iobPoints,
  66. tintColor: colors.insulinTint,
  67. gestureRecognizer: gestureRecognizer
  68. )
  69. }
  70. let layers: [ChartLayer?] = [
  71. gridLayer,
  72. xAxisLayer,
  73. yAxisLayer,
  74. zeroGuidelineLayer,
  75. iobChartCache?.highlightLayer,
  76. iobArea,
  77. iobLine,
  78. ]
  79. return Chart(frame: frame, innerFrame: innerFrame, settings: chartSettings, layers: layers.compactMap { $0 })
  80. }
  81. }
  82. public extension IOBChart {
  83. func setIOBValues(_ iobValues: [InsulinValue]) {
  84. let dateFormatter = DateFormatter(timeStyle: .short)
  85. let doseFormatter = NumberFormatter.dose
  86. iobPoints = iobValues.map {
  87. return ChartPoint(
  88. x: ChartAxisValueDate(date: $0.startDate, formatter: dateFormatter),
  89. y: ChartAxisValueDoubleUnit($0.value, unitString: Self.chartUnit.shortLocalizedUnitString(), formatter: doseFormatter)
  90. )
  91. }
  92. }
  93. }