Chart.swift 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. //
  2. // Chart.swift
  3. // LoopFollow
  4. //
  5. // Created by Jon Fawcett on 6/3/20.
  6. // Copyright © 2020 Jon Fawcett. All rights reserved.
  7. //
  8. import Foundation
  9. import Charts
  10. final class OverrideFillFormatter: FillFormatter {
  11. func getFillLinePosition(dataSet: Charts.LineChartDataSetProtocol, dataProvider: Charts.LineChartDataProvider) -> CGFloat {
  12. return CGFloat(dataSet.entryForIndex(0)!.y)
  13. //return 375
  14. }
  15. }
  16. final class basalFillFormatter: FillFormatter {
  17. func getFillLinePosition(dataSet: Charts.LineChartDataSetProtocol, dataProvider: Charts.LineChartDataProvider) -> CGFloat {
  18. return 0
  19. }
  20. }
  21. final class ChartXValueFormatter: AxisValueFormatter {
  22. func stringForValue(_ value: Double, axis: AxisBase?) -> String {
  23. let dateFormatter = DateFormatter()
  24. //let timezoneOffset = TimeZone.current.secondsFromGMT()
  25. //let epochTimezoneOffset = value + Double(timezoneOffset)
  26. if dateTimeUtils.is24Hour() {
  27. dateFormatter.setLocalizedDateFormatFromTemplate("HH:mm")
  28. } else {
  29. dateFormatter.setLocalizedDateFormatFromTemplate("hh:mm")
  30. }
  31. //let date = Date(timeIntervalSince1970: epochTimezoneOffset)
  32. let date = Date(timeIntervalSince1970: value)
  33. let formattedDate = dateFormatter.string(from: date)
  34. return formattedDate
  35. }
  36. }
  37. final class ChartYDataValueFormatter: ValueFormatter {
  38. func stringForValue(_ value: Double, entry: ChartDataEntry, dataSetIndex: Int, viewPortHandler: ViewPortHandler?) -> String {
  39. if entry.data != nil {
  40. return entry.data as? String ?? ""
  41. } else {
  42. return ""
  43. }
  44. }
  45. }
  46. final class ChartYOverrideValueFormatter: ValueFormatter {
  47. func stringForValue(_ value: Double, entry: ChartDataEntry, dataSetIndex: Int, viewPortHandler: ViewPortHandler?) -> String {
  48. if entry.data != nil {
  49. return entry.data as? String ?? ""
  50. } else {
  51. return ""
  52. }
  53. }
  54. }
  55. final class ChartYMMOLValueFormatter: AxisValueFormatter {
  56. func stringForValue(_ value: Double, axis: AxisBase?) -> String {
  57. return Localizer.toDisplayUnits(String(value))
  58. }
  59. }
  60. class PillMarker: MarkerImage {
  61. private (set) var color: UIColor
  62. private (set) var font: UIFont
  63. private (set) var textColor: UIColor
  64. private var labelText: String = ""
  65. private var attrs: [NSAttributedString.Key: AnyObject]!
  66. static let formatter: DateComponentsFormatter = {
  67. let f = DateComponentsFormatter()
  68. f.allowedUnits = [.minute, .second]
  69. f.unitsStyle = .short
  70. return f
  71. }()
  72. init(color: UIColor, font: UIFont, textColor: UIColor) {
  73. self.color = color
  74. self.font = font
  75. self.textColor = textColor
  76. let paragraphStyle = NSMutableParagraphStyle()
  77. paragraphStyle.alignment = .center
  78. attrs = [.font: font, .paragraphStyle: paragraphStyle, .foregroundColor: textColor, .baselineOffset: NSNumber(value: -4)]
  79. super.init()
  80. }
  81. override func draw(context: CGContext, point: CGPoint) {
  82. // custom padding around text
  83. let labelWidth = labelText.size(withAttributes: attrs).width + 10
  84. // if you modify labelHeigh you will have to tweak baselineOffset in attrs
  85. let labelHeight = labelText.size(withAttributes: attrs).height + 4
  86. // place pill above the marker, centered along x
  87. var rectangle = CGRect(x: point.x, y: point.y, width: labelWidth, height: labelHeight)
  88. rectangle.origin.x -= rectangle.width / 2.0
  89. var spacing: CGFloat = 20
  90. if point.y < 300 { spacing = -40 }
  91. rectangle.origin.y -= rectangle.height + spacing
  92. // rounded rect
  93. let clipPath = UIBezierPath(roundedRect: rectangle, cornerRadius: 6.0).cgPath
  94. context.addPath(clipPath)
  95. context.setFillColor(UIColor.secondarySystemBackground.cgColor)
  96. context.setStrokeColor(UIColor.label.cgColor)
  97. context.closePath()
  98. context.drawPath(using: .fillStroke)
  99. // add the text
  100. labelText.draw(with: rectangle, options: .usesLineFragmentOrigin, attributes: attrs, context: nil)
  101. }
  102. override func refreshContent(entry: ChartDataEntry, highlight: Highlight) {
  103. if entry.data != nil {
  104. //var multiplier = entry.data as! Double * 100.0
  105. //labelText = String(format: "%.0f%%", multiplier)
  106. labelText = entry.data as? String ?? ""
  107. } else {
  108. labelText = String(entry.y)
  109. }
  110. }
  111. private func customString(_ value: Double) -> String {
  112. let formattedString = PillMarker.formatter.string(from: TimeInterval(value))!
  113. // using this to convert the left axis values formatting, ie 2 min
  114. return "\(formattedString)"
  115. }
  116. }