Skip to content

Commit

Permalink
Move label settings to ReferenceLines until overhaul of labels
Browse files Browse the repository at this point in the history
  • Loading branch information
philackm committed Jul 6, 2017
1 parent fb98c63 commit 1ace1e6
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 115 deletions.
19 changes: 16 additions & 3 deletions Classes/Reference/ReferenceLines.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ open class ReferenceLines {
}
/// Where the labels should be displayed on the reference lines.
open var referenceLinePosition = ScrollableGraphViewReferenceLinePosition.left
/// The type of reference lines. Currently only .Cover is available.
open var referenceLineType = ScrollableGraphViewReferenceLineType.cover

@IBInspectable open var positionType = ReferenceLinePositioningType.relative
@IBInspectable open var relativePositions: [Double] = [0.25, 0.5, 0.75]
Expand All @@ -37,7 +35,6 @@ open class ReferenceLines {
/// Whether or not to add units specified by the referenceLineUnits variable to the labels on the intermediate reference lines.
@IBInspectable open var shouldAddUnitsToIntermediateReferenceLineLabels: Bool = false


// Reference Line Labels
// #####################

Expand All @@ -54,6 +51,22 @@ open class ReferenceLines {
@IBInspectable open var referenceLineNumberOfDecimalPlaces: Int = 0
/// The NSNumberFormatterStyle that reference lines should use to display
@IBInspectable open var referenceLineNumberStyle: NumberFormatter.Style = .none

// Data Point Labels // TODO: Refactor these into their own settings and allow for more label options (positioning)
// ################################################################################################################

/// Whether or not to show the labels on the x-axis for each point.
@IBInspectable open var shouldShowLabels: Bool = true
/// How far from the "minimum" reference line the data point labels should be rendered.
@IBInspectable open var dataPointLabelTopMargin: CGFloat = 10
/// How far from the bottom of the view the data point labels should be rendered.
@IBInspectable open var dataPointLabelBottomMargin: CGFloat = 0
/// The font for the data point labels.
@IBInspectable open var dataPointLabelColor: UIColor = UIColor.black
/// The colour for the data point labels.
open var dataPointLabelFont: UIFont? = UIFont.systemFont(ofSize: 10)
/// Used to force the graph to show every n-th dataPoint label
@IBInspectable open var dataPointLabelsSparsity: Int = 1
}

public enum ReferenceLinePositioningType {
Expand Down
207 changes: 103 additions & 104 deletions Classes/ScrollableGraphView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,6 @@ import UIKit
// Graph Range
// ###########

/// If this is set to true, then the range will automatically be detected from the data the graph is given.
//@IBInspectable open var shouldAutomaticallyDetectRange: Bool = false
// This is removed and now the user is responsible for setting a decent range.
// This is because we don't want to have to check all of the data unless we have to.
// Automatically checking the range requires us to inspect every data point at the start
// If we dont do this, then we only ever touch the data that the user sees on screen.


/// Forces the graph's minimum to always be zero. Used in conjunction with shouldAutomaticallyDetectRange or shouldAdaptRange, if you want to force the minimum to stay at 0 rather than the detected minimum.
@IBInspectable open var shouldRangeAlwaysStartAtZero: Bool = false // Used in conjunction with shouldAutomaticallyDetectRange, if you want to force the min to stay at 0.
/// The minimum value for the y-axis. This is ignored when shouldAutomaticallyDetectRange or shouldAdaptRange = true
Expand All @@ -68,22 +60,6 @@ import UIKit
/// Whether or not the graph should animate to their positions when the graph is first displayed.
@IBInspectable open var shouldAnimateOnStartup: Bool = true

// Data Point Labels // TODO: These can go into reference line settings.
// #####################################################################

/// Whether or not to show the labels on the x-axis for each point.
@IBInspectable open var shouldShowLabels: Bool = true
/// How far from the "minimum" reference line the data point labels should be rendered.
@IBInspectable open var dataPointLabelTopMargin: CGFloat = 10
/// How far from the bottom of the view the data point labels should be rendered.
@IBInspectable open var dataPointLabelBottomMargin: CGFloat = 0
/// The font for the data point labels.
@IBInspectable open var dataPointLabelColor: UIColor = UIColor.black
/// The colour for the data point labels.
open var dataPointLabelFont: UIFont? = UIFont.systemFont(ofSize: 10)
/// Used to force the graph to show every n-th dataPoint label
@IBInspectable open var dataPointLabelsSparsity: Int = 1

// Reference Line Settings
// #######################

Expand Down Expand Up @@ -311,8 +287,8 @@ import UIKit
}

// TODO: Plot layer ordering.
// TODO: Plot removal.
private func addDrawingLayersForPlots(inViewport viewport: CGRect) {

for plot in plots {
addSubLayers(layers: plot.layers(forViewport: viewport))
}
Expand All @@ -333,25 +309,27 @@ import UIKit
return
}

let viewport = CGRect(x: 0, y: 0, width: viewportWidth, height: viewportHeight)
var referenceLineBottomMargin = bottomMargin

// Have to adjust the bottom line if we are showing data point labels (x-axis).
if(shouldShowLabels && dataPointLabelFont != nil) {
referenceLineBottomMargin += (dataPointLabelFont!.pointSize + dataPointLabelTopMargin + dataPointLabelBottomMargin)
if(referenceLines.shouldShowReferenceLines) {
let viewport = CGRect(x: 0, y: 0, width: viewportWidth, height: viewportHeight)
var referenceLineBottomMargin = bottomMargin

// Have to adjust the bottom line if we are showing data point labels (x-axis).
if(referenceLines.shouldShowLabels && referenceLines.dataPointLabelFont != nil) {
referenceLineBottomMargin += (referenceLines.dataPointLabelFont!.pointSize + referenceLines.dataPointLabelTopMargin + referenceLines.dataPointLabelBottomMargin)
}

referenceLineView = ReferenceLineDrawingView(
frame: viewport,
topMargin: topMargin,
bottomMargin: referenceLineBottomMargin,
referenceLineColor: referenceLines.referenceLineColor,
referenceLineThickness: referenceLines.referenceLineThickness,
referenceLineSettings: referenceLines)

referenceLineView?.set(range: self.range)

self.addSubview(referenceLineView!)
}

referenceLineView = ReferenceLineDrawingView(
frame: viewport,
topMargin: topMargin,
bottomMargin: referenceLineBottomMargin,
referenceLineColor: referenceLines.referenceLineColor,
referenceLineThickness: referenceLines.referenceLineThickness,
referenceLineSettings: referenceLines)

referenceLineView?.set(range: self.range)

self.addSubview(referenceLineView!)
}

// If the view has changed we have to make sure we're still displaying the right data.
Expand All @@ -364,7 +342,11 @@ import UIKit
var availableGraphHeight = frame.height
availableGraphHeight = availableGraphHeight - topMargin - bottomMargin

if(shouldShowLabels && dataPointLabelFont != nil) { availableGraphHeight -= (dataPointLabelFont!.pointSize + dataPointLabelTopMargin + dataPointLabelBottomMargin) }
if let referenceLines = referenceLines {
if(referenceLines.shouldShowLabels && referenceLines.dataPointLabelFont != nil) {
availableGraphHeight -= (referenceLines.dataPointLabelFont!.pointSize + referenceLines.dataPointLabelTopMargin + referenceLines.dataPointLabelBottomMargin)
}
}

if availableGraphHeight > 0 {
updateUI()
Expand Down Expand Up @@ -713,10 +695,12 @@ import UIKit

updatePaths()

if(shouldShowLabels) {
let deactivatedLabelPoints = filterPointsForLabels(fromPoints: deactivatedPoints)
let activatedLabelPoints = filterPointsForLabels(fromPoints: activatedPoints)
updateLabels(deactivatedPoints: deactivatedLabelPoints, activatedPoints: activatedLabelPoints)
if let ref = self.referenceLines {
if(ref.shouldShowLabels) {
let deactivatedLabelPoints = filterPointsForLabels(fromPoints: deactivatedPoints)
let activatedLabelPoints = filterPointsForLabels(fromPoints: activatedPoints)
updateLabels(deactivatedPoints: deactivatedLabelPoints, activatedPoints: activatedLabelPoints)
}
}
}

Expand Down Expand Up @@ -760,9 +744,58 @@ import UIKit
}
}

// Returns the indices of any points that became inactive (that is, "off screen"). (No order)
private func determineDeactivatedPoints() -> [Int] {
let prevSet = Set(previousActivePointsInterval)
let currSet = Set(activePointsInterval)

let deactivatedPoints = prevSet.subtracting(currSet)

return Array(deactivatedPoints)
}

// Returns the indices of any points that became active (on screen). (No order)
private func determineActivatedPoints() -> [Int] {
let prevSet = Set(previousActivePointsInterval)
let currSet = Set(activePointsInterval)

let activatedPoints = currSet.subtracting(prevSet)

return Array(activatedPoints)
}

// Animations

private func startAnimations(withStaggerValue stagger: Double = 0) {
var pointsToAnimate = 0 ..< 0

#if !TARGET_INTERFACE_BUILDER
if (shouldAnimateOnAdapt || (dataNeedsReloading && shouldAnimateOnStartup)) {
pointsToAnimate = activePointsInterval
}
#endif

for plot in plots {
let dataForPointsToAnimate = getData(forPlot: plot, andActiveInterval: pointsToAnimate)
plot.startAnimations(forPoints: pointsToAnimate, withData: dataForPointsToAnimate, withStaggerValue: stagger) // CHANGED
}
}

private func stopAnimations() {
for plot in plots {
plot.dequeueAllAnimations()
}
}

// Labels. // TODO in 4.1, refactor all label adding & positioning code.

// Update any labels for any new points that have been activated and deactivated.
private func updateLabels(deactivatedPoints: [Int], activatedPoints: [Int]) {

guard let ref = self.referenceLines else {
return
}

// Disable any labels for the deactivated points.
for point in deactivatedPoints {
labelPool.deactivateLabel(forPointIndex: point)
Expand All @@ -773,8 +806,8 @@ import UIKit
let label = labelPool.activateLabel(forPointIndex: point)

label.text = (dataSource?.label(atIndex: point) ?? "")
label.textColor = dataPointLabelColor
label.font = dataPointLabelFont
label.textColor = ref.dataPointLabelColor
label.font = ref.dataPointLabelFont

label.sizeToFit()

Expand All @@ -783,71 +816,39 @@ import UIKit
let rangeMin = (shouldAdaptRange) ? self.range.min : self.rangeMin
let position = calculatePosition(atIndex: point, value: rangeMin)

label.frame = CGRect(origin: CGPoint(x: position.x - label.frame.width / 2, y: position.y + dataPointLabelTopMargin), size: label.frame.size)
label.frame = CGRect(origin: CGPoint(x: position.x - label.frame.width / 2, y: position.y + ref.dataPointLabelTopMargin), size: label.frame.size)

let _ = labelsView.subviews.filter { $0.frame == label.frame }.map { $0.removeFromSuperview() }

labelsView.addSubview(label)
}
}

private func repositionActiveLabels() {

guard let ref = self.referenceLines else {
return
}

for label in labelPool.activeLabels {

let rangeMin = (shouldAdaptRange) ? self.range.min : self.rangeMin
let position = calculatePosition(atIndex: 0, value: rangeMin)

label.frame.origin.y = position.y + dataPointLabelTopMargin
label.frame.origin.y = position.y + ref.dataPointLabelTopMargin
}
}

// Returns the indices of any points that became inactive (that is, "off screen"). (No order)
private func determineDeactivatedPoints() -> [Int] {
let prevSet = Set(previousActivePointsInterval)
let currSet = Set(activePointsInterval)

let deactivatedPoints = prevSet.subtracting(currSet)

return Array(deactivatedPoints)
}

// Returns the indices of any points that became active (on screen). (No order)
private func determineActivatedPoints() -> [Int] {
let prevSet = Set(previousActivePointsInterval)
let currSet = Set(activePointsInterval)

let activatedPoints = currSet.subtracting(prevSet)

return Array(activatedPoints)
}

private func filterPointsForLabels(fromPoints points:[Int]) -> [Int] {

if(self.dataPointLabelsSparsity == 1) {
guard let ref = self.referenceLines else {
return points
}
return points.filter({ $0 % self.dataPointLabelsSparsity == 0 })
}

private func startAnimations(withStaggerValue stagger: Double = 0) {
var pointsToAnimate = 0 ..< 0

#if !TARGET_INTERFACE_BUILDER
if (shouldAnimateOnAdapt || (dataNeedsReloading && shouldAnimateOnStartup)) {
pointsToAnimate = activePointsInterval
}
#endif

for plot in plots {
let dataForPointsToAnimate = getData(forPlot: plot, andActiveInterval: pointsToAnimate)
plot.startAnimations(forPoints: pointsToAnimate, withData: dataForPointsToAnimate, withStaggerValue: stagger) // CHANGED
}
}

private func stopAnimations() {
for plot in plots {
plot.dequeueAllAnimations()
if(ref.dataPointLabelsSparsity == 1) {
return points
}
return points.filter({ $0 % ref.dataPointLabelsSparsity == 0 })
}

// MARK: - Drawing Delegate
Expand All @@ -863,15 +864,21 @@ import UIKit
let rangeMin = (shouldAdaptRange) ? self.range.min : self.rangeMin

// y = the y co-ordinate in the view for the value in the graph
// ( ( value - max ) ) value = the value on the graph for which we want to know its corresponding location on the y axis in the view
// value = the value on the graph for which we want to know its
// ( ( value - max ) ) corresponding location on the y axis in the view
// y = ( ( ----------- ) * graphHeight ) + topMargin t = the top margin
// ( ( min - max ) ) h = the height of the graph space without margins
// min = the range's current mininum
// max = the range's current maximum

// Calculate the position on in the view for the value specified.
var graphHeight = viewportHeight - topMargin - bottomMargin
if(shouldShowLabels && dataPointLabelFont != nil) { graphHeight -= (dataPointLabelFont!.pointSize + dataPointLabelTopMargin + dataPointLabelBottomMargin) }

if let ref = self.referenceLines {
if(ref.shouldShowLabels && ref.dataPointLabelFont != nil) {
graphHeight -= (ref.dataPointLabelFont!.pointSize + ref.dataPointLabelTopMargin + ref.dataPointLabelBottomMargin)
}
}

let x = (CGFloat(index) * dataPointSpacing) + leftmostPointPadding
let y = (CGFloat((value - rangeMax) / (rangeMin - rangeMax)) * graphHeight) + topMargin
Expand All @@ -891,14 +898,6 @@ import UIKit
return (leftmostPointPadding: leftmostPointPadding, rightmostPointPadding: rightmostPointPadding)
}

// TODO, make the drawing layer get this from its owning plot instead of going all the away around.
/*
internal func graphPoint(forIndex index: Int) -> GraphPoint {
// TODO: For testing: Need to just return any of the graph points, because they should be the same at this point.
return plots.first?.graphPoint(forIndex: index) ?? GraphPoint()
}
*/

internal func currentViewport() -> CGRect {
return CGRect(x: 0, y: 0, width: viewportWidth, height: viewportHeight)
}
Expand Down
Loading

0 comments on commit 1ace1e6

Please sign in to comment.