ChartPointsContextFillLayer.swift 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. //
  2. // ChartPointsContextFillLayer.swift
  3. // Loop
  4. //
  5. // Copyright © 2017 LoopKit Authors. All rights reserved.
  6. //
  7. import SwiftCharts
  8. import CoreGraphics
  9. import UIKit
  10. struct ChartPointsFill {
  11. let chartPoints: [ChartPoint]
  12. let fillColor: UIColor
  13. let createContainerPoints: Bool
  14. let blendMode: CGBlendMode
  15. fileprivate var screenPoints: [CGPoint] = []
  16. init?(chartPoints: [ChartPoint], fillColor: UIColor, createContainerPoints: Bool = true, blendMode: CGBlendMode = .normal) {
  17. guard chartPoints.count > 1 else {
  18. return nil;
  19. }
  20. var chartPoints = chartPoints
  21. if createContainerPoints {
  22. // Create a container line at value position 0
  23. if let first = chartPoints.first {
  24. chartPoints.insert(ChartPoint(x: first.x, y: ChartAxisValueInt(0)), at: 0)
  25. }
  26. if let last = chartPoints.last {
  27. chartPoints.append(ChartPoint(x: last.x, y: ChartAxisValueInt(0)))
  28. }
  29. }
  30. self.chartPoints = chartPoints
  31. self.fillColor = fillColor
  32. self.createContainerPoints = createContainerPoints
  33. self.blendMode = blendMode
  34. }
  35. var areaPath: UIBezierPath {
  36. let path = UIBezierPath()
  37. if let point = screenPoints.first {
  38. path.move(to: point)
  39. }
  40. for point in screenPoints.dropFirst() {
  41. path.addLine(to: point)
  42. }
  43. return path
  44. }
  45. }
  46. final class ChartPointsFillsLayer: ChartCoordsSpaceLayer {
  47. let fills: [ChartPointsFill]
  48. init?(xAxis: ChartAxis, yAxis: ChartAxis, fills: [ChartPointsFill?]) {
  49. self.fills = fills.compactMap({ $0 })
  50. guard fills.count > 0 else {
  51. return nil
  52. }
  53. super.init(xAxis: xAxis, yAxis: yAxis)
  54. }
  55. override func chartInitialized(chart: Chart) {
  56. super.chartInitialized(chart: chart)
  57. let view = ChartPointsFillsView(
  58. frame: chart.bounds,
  59. chartPointsFills: fills.map { (fill) -> ChartPointsFill in
  60. var fill = fill
  61. fill.screenPoints = fill.chartPoints.map { (point) -> CGPoint in
  62. return modelLocToScreenLoc(x: point.x.scalar, y: point.y.scalar)
  63. }
  64. return fill
  65. }
  66. )
  67. chart.addSubview(view)
  68. }
  69. }
  70. class ChartPointsFillsView: UIView {
  71. let chartPointsFills: [ChartPointsFill]
  72. var allowsAntialiasing = false
  73. init(frame: CGRect, chartPointsFills: [ChartPointsFill]) {
  74. self.chartPointsFills = chartPointsFills
  75. super.init(frame: frame)
  76. backgroundColor = .clear
  77. }
  78. required init?(coder aDecoder: NSCoder) {
  79. fatalError("init(coder:) has not been implemented")
  80. }
  81. override func draw(_ rect: CGRect) {
  82. guard let context = UIGraphicsGetCurrentContext() else { return }
  83. context.saveGState()
  84. context.setAllowsAntialiasing(allowsAntialiasing)
  85. for fill in chartPointsFills {
  86. context.setFillColor(fill.fillColor.cgColor)
  87. fill.areaPath.fill(with: fill.blendMode, alpha: 1)
  88. }
  89. context.restoreGState()
  90. }
  91. }