From 20ae27026c5e6957d753f3ef0d5b73b6bf249622 Mon Sep 17 00:00:00 2001 From: Weiran Zhang Date: Sat, 14 Jul 2018 13:41:47 +0100 Subject: [PATCH 1/3] Added plot labels --- Classes/Plots/Plot.swift | 10 +++++ .../ScrollableGraphViewDataSource.swift | 7 ++++ Classes/ScrollableGraphView.swift | 40 +++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/Classes/Plots/Plot.swift b/Classes/Plots/Plot.swift index e2cd2f4..211a196 100644 --- a/Classes/Plots/Plot.swift +++ b/Classes/Plots/Plot.swift @@ -27,6 +27,16 @@ open class Plot { /// If adaptAnimationType is set to .Custom, then this is the easing function you would like applied for the animation. open var customAnimationEasingFunction: ((_ t: Double) -> Double)? + // Labels + // ##################### + + /// The font to be used for the value label. + open var labelFont = UIFont.systemFont(ofSize: 8) + /// The colour of the value label font. + open var labelColor: UIColor = UIColor.black + /// How far to offset the vertical position of the label. + open var labelVerticalOffset: CGFloat = 0 + // Private Animation State // ####################### diff --git a/Classes/Protocols/ScrollableGraphViewDataSource.swift b/Classes/Protocols/ScrollableGraphViewDataSource.swift index 3c778e1..1fba631 100644 --- a/Classes/Protocols/ScrollableGraphViewDataSource.swift +++ b/Classes/Protocols/ScrollableGraphViewDataSource.swift @@ -5,4 +5,11 @@ public protocol ScrollableGraphViewDataSource : class { func value(forPlot plot: Plot, atIndex pointIndex: Int) -> Double func label(atIndex pointIndex: Int) -> String func numberOfPoints() -> Int // This now forces the same number of points in each plot. + func plotLabel(shouldShowPlotLabel plot: Plot, atIndex pointIndex: Int) -> Bool + func plotLabel(forPlot plot: Plot, atIndex pointIndex: Int) -> String? +} + +extension ScrollableGraphViewDataSource { + func plotLabel(shouldShowPlotLabel plot: Plot, atIndex pointIndex: Int) -> Bool { return false } + func plotLabel(forPlot plot: Plot, atIndex pointIndex: Int) -> String? { return nil } } diff --git a/Classes/ScrollableGraphView.swift b/Classes/ScrollableGraphView.swift index 9dc5d18..5a0b75c 100644 --- a/Classes/ScrollableGraphView.swift +++ b/Classes/ScrollableGraphView.swift @@ -94,6 +94,7 @@ import UIKit // Labels private var labelsView = UIView() private var labelPool = LabelPool() + private var plotLabelPool = [LabelPool]() // Data Source weak open var dataSource: ScrollableGraphViewDataSource? { @@ -478,6 +479,7 @@ import UIKit private func addPlotToGraph(plot: Plot, activePointsInterval: CountableRange) { plot.graphViewDrawingDelegate = self self.plots.append(plot) + self.plotLabelPool.append(LabelPool()) initPlot(plot: plot, activePointsInterval: activePointsInterval) startAnimations(withStaggerValue: 0.15) } @@ -708,6 +710,7 @@ import UIKit let deactivatedLabelPoints = filterPointsForLabels(fromPoints: deactivatedPoints) let activatedLabelPoints = filterPointsForLabels(fromPoints: activatedPoints) updateLabels(deactivatedPoints: deactivatedLabelPoints, activatedPoints: activatedLabelPoints) + updatePlotLabels(deactivatedPoints: deactivatedLabelPoints, activatedPoints: activatedLabelPoints) } } } @@ -833,6 +836,42 @@ import UIKit } } + private func updatePlotLabels(deactivatedPoints: [Int], activatedPoints: [Int]) { + guard let dataSource = self.dataSource else { + return + } + + for (index, plot) in plots.enumerated() { + let labelPool = plotLabelPool[index] + + // Disable any labels for the deactivated points. + for point in deactivatedPoints { + labelPool.deactivateLabel(forPointIndex: point) + } + + // Grab an unused label and update it to the right position for the newly activated poitns + for point in activatedPoints { + guard let plotLabelText = dataSource.plotLabel(forPlot: plot, atIndex: point) else { + continue + } + let label = labelPool.activateLabel(forPointIndex: point) + label.text = plotLabelText + label.isHidden = !dataSource.plotLabel(shouldShowPlotLabel: plot, atIndex: point) + label.textColor = plot.labelColor + label.font = plot.labelFont + + label.sizeToFit() + + let position = calculatePosition(atIndex: point, value: dataSource.value(forPlot: plot, atIndex: point)) + label.frame = CGRect(origin: CGPoint(x: position.x - label.frame.width / 2, y: position.y - label.frame.height + plot.labelVerticalOffset), size: label.frame.size) + + _ = labelsView.subviews.filter { $0.frame == label.frame }.map { $0.removeFromSuperview() } + + labelsView.addSubview(label) + } + } + } + private func updateLabelsForCurrentInterval() { // Have to ensure that the labels are added if we are supposed to be showing them. if let ref = self.referenceLines { @@ -845,6 +884,7 @@ import UIKit let filteredPoints = filterPointsForLabels(fromPoints: activatedPoints) updateLabels(deactivatedPoints: filteredPoints, activatedPoints: filteredPoints) + updatePlotLabels(deactivatedPoints: filteredPoints, activatedPoints: filteredPoints) } } } From 7cd3c80d8da9cad56f98ba10f25860b66eb2d427 Mon Sep 17 00:00:00 2001 From: Weiran Zhang Date: Sat, 14 Jul 2018 13:50:26 +0100 Subject: [PATCH 2/3] Added plot label example --- .../GraphView/ViewController.swift | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/graphview_example_code/GraphView/ViewController.swift b/graphview_example_code/GraphView/ViewController.swift index 4a40206..5b33b72 100644 --- a/graphview_example_code/GraphView/ViewController.swift +++ b/graphview_example_code/GraphView/ViewController.swift @@ -57,6 +57,28 @@ class ViewController: UIViewController, ScrollableGraphViewDataSource { // off many graphs with different plots, we are using one big switch // statement. func value(forPlot plot: Plot, atIndex pointIndex: Int) -> Double { + return dataValue(forPlot: plot, atIndex: pointIndex) + } + + func label(atIndex pointIndex: Int) -> String { + // Ensure that you have a label to return for the index + return xAxisLabels[pointIndex] + } + + // Uncomment the below to enable plot labels +// func plotLabel(forPlot plot: Plot, atIndex pointIndex: Int) -> String? { +// return "\(dataValue(forPlot: plot, atIndex: pointIndex))" +// } +// +// func plotLabel(shouldShowPlotLabel plot: Plot, atIndex pointIndex: Int) -> Bool { +// return true +// } + + func numberOfPoints() -> Int { + return numberOfDataItems + } + + func dataValue(forPlot plot: Plot, atIndex pointIndex: Int) -> Double { switch(plot.identifier) { @@ -89,15 +111,6 @@ class ViewController: UIViewController, ScrollableGraphViewDataSource { } } - func label(atIndex pointIndex: Int) -> String { - // Ensure that you have a label to return for the index - return xAxisLabels[pointIndex] - } - - func numberOfPoints() -> Int { - return numberOfDataItems - } - // Creating Different Kinds of Graphs // ################################## From 658673ce835f8ffc6b32e590db797bee3c6769fb Mon Sep 17 00:00:00 2001 From: Weiran Zhang Date: Sat, 11 Aug 2018 11:21:17 +0100 Subject: [PATCH 3/3] Remove old plot labels by comparing frame origin rather than just frame --- Classes/ScrollableGraphView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/ScrollableGraphView.swift b/Classes/ScrollableGraphView.swift index 5a0b75c..f61975c 100644 --- a/Classes/ScrollableGraphView.swift +++ b/Classes/ScrollableGraphView.swift @@ -865,7 +865,7 @@ import UIKit let position = calculatePosition(atIndex: point, value: dataSource.value(forPlot: plot, atIndex: point)) label.frame = CGRect(origin: CGPoint(x: position.x - label.frame.width / 2, y: position.y - label.frame.height + plot.labelVerticalOffset), size: label.frame.size) - _ = labelsView.subviews.filter { $0.frame == label.frame }.map { $0.removeFromSuperview() } + _ = labelsView.subviews.filter { $0.frame.origin == label.frame.origin }.map { $0.removeFromSuperview() } labelsView.addSubview(label) }