diff --git a/Source/Chart.swift b/Source/Chart.swift index 763b830a1..4ae6d4bad 100644 --- a/Source/Chart.swift +++ b/Source/Chart.swift @@ -19,7 +19,7 @@ public protocol ChartDelegate: class { - parameter left: The distance from the left side of the chart. */ - func didTouchChart(_ chart: Chart, indexes: [Int?], x: Float, left: CGFloat) + func didTouchChart(_ chart: Chart, indexes: [Int?], x: Double, left: CGFloat) /** Tells the delegate that the user finished touching the chart. The user will @@ -43,7 +43,7 @@ public protocol ChartDelegate: class { /** Represent the x- and the y-axis values for each point in a chart series. */ -typealias ChartPoint = (x: Float, y: Float) +typealias ChartPoint = (x: Double, y: Double) public enum ChartLabelOrientation { case horizontal @@ -71,12 +71,12 @@ open class Chart: UIControl { The values to display as labels on the x-axis. You can format these values with the `xLabelFormatter` attribute. As default, it will display the values of the series which has the most data. */ - open var xLabels: [Float]? + open var xLabels: [Double]? /** Formatter for the labels on the x-axis. The `index` represents the `xLabels` index, `value` its value: */ - open var xLabelsFormatter = { (labelIndex: Int, labelValue: Float) -> String in + open var xLabelsFormatter = { (labelIndex: Int, labelValue: Double) -> String in String(Int(labelValue)) } @@ -99,12 +99,12 @@ open class Chart: UIControl { Values to display as labels of the y-axis. If not specified, will display the lowest, the middle and the highest values. */ - open var yLabels: [Float]? + open var yLabels: [Double]? /** Formatter for the labels on the y-axis. */ - open var yLabelsFormatter = { (labelIndex: Int, labelValue: Float) -> String in + open var yLabelsFormatter = { (labelIndex: Int, labelValue: Double) -> String in String(Int(labelValue)) } @@ -168,22 +168,22 @@ open class Chart: UIControl { /** Custom minimum value for the x-axis. */ - open var minX: Float? + open var minX: Double? /** Custom minimum value for the y-axis. */ - open var minY: Float? + open var minY: Double? /** Custom maximum value for the x-axis. */ - open var maxX: Float? + open var maxX: Double? /** Custom maximum value for the y-axis. */ - open var maxY: Float? + open var maxY: Double? /** Color for the highlight line. @@ -278,7 +278,7 @@ open class Chart: UIControl { /** Returns the value for the specified series at the given index */ - open func valueForSeries(_ seriesIndex: Int, atIndex dataIndex: Int?) -> Float? { + open func valueForSeries(_ seriesIndex: Int, atIndex dataIndex: Int?) -> Double? { if dataIndex == nil { return nil } let series = self.series[seriesIndex] as ChartSeries return series.data[dataIndex!].y @@ -328,8 +328,8 @@ open class Chart: UIControl { let segments = Chart.segmentLine(series.data as ChartLineSegment, zeroLevel: series.colors.zeroLevel) segments.forEach({ segment in - let scaledXValues = scaleValuesOnXAxis( segment.map({ return $0.x }) ) - let scaledYValues = scaleValuesOnYAxis( segment.map({ return $0.y }) ) + let scaledXValues = scaleValuesOnXAxis( segment.map { $0.x } ) + let scaledYValues = scaleValuesOnYAxis( segment.map { $0.y } ) if series.line { drawLine(scaledXValues, yValues: scaledYValues, seriesIndex: index) @@ -354,7 +354,6 @@ open class Chart: UIControl { // MARK: - Scaling fileprivate func getMinMax() -> (min: ChartPoint, max: ChartPoint) { - // Start with user-provided values var min = (x: minX, y: minY) @@ -363,15 +362,13 @@ open class Chart: UIControl { // Check in datasets for series in self.series { - let xValues = series.data.map({ (point: ChartPoint) -> Float in - return point.x }) - let yValues = series.data.map({ (point: ChartPoint) -> Float in - return point.y }) + let xValues = series.data.map { $0.x } + let yValues = series.data.map { $0.y } - let newMinX = xValues.min()! - let newMinY = yValues.min()! - let newMaxX = xValues.max()! - let newMaxY = yValues.max()! + let newMinX = xValues.minOrZero() + let newMinY = yValues.minOrZero() + let newMaxX = xValues.maxOrZero() + let newMaxY = yValues.maxOrZero() if min.x == nil || newMinX < min.x! { min.x = newMinX } if min.y == nil || newMinY < min.y! { min.y = newMinY } @@ -381,16 +378,16 @@ open class Chart: UIControl { // Check in labels - if xLabels != nil { - let newMinX = (xLabels!).min()! - let newMaxX = (xLabels!).max()! + if let xLabels = self.xLabels { + let newMinX = xLabels.minOrZero() + let newMaxX = xLabels.maxOrZero() if min.x == nil || newMinX < min.x! { min.x = newMinX } if max.x == nil || newMaxX > max.x! { max.x = newMaxX } } - if yLabels != nil { - let newMinY = (yLabels!).min()! - let newMaxY = (yLabels!).max()! + if let yLabels = self.yLabels { + let newMinY = yLabels.minOrZero() + let newMaxY = yLabels.maxOrZero() if min.y == nil || newMinY < min.y! { min.y = newMinY } if max.y == nil || newMaxY > max.y! { max.y = newMaxY } } @@ -401,13 +398,12 @@ open class Chart: UIControl { if max.y == nil { max.y = 0 } return (min: (x: min.x!, y: min.y!), max: (x: max.x!, max.y!)) - } - fileprivate func scaleValuesOnXAxis(_ values: [Float]) -> [Float] { - let width = Float(drawingWidth) + fileprivate func scaleValuesOnXAxis(_ values: [Double]) -> [Double] { + let width = Double(drawingWidth) - var factor: Float + var factor: Double if max.x - min.x == 0 { factor = 0 } else { @@ -418,47 +414,44 @@ open class Chart: UIControl { return scaled } - fileprivate func scaleValuesOnYAxis(_ values: [Float]) -> [Float] { - - let height = Float(drawingHeight) - var factor: Float + fileprivate func scaleValuesOnYAxis(_ values: [Double]) -> [Double] { + let height = Double(drawingHeight) + var factor: Double if max.y - min.y == 0 { factor = 0 } else { factor = height / (max.y - min.y) } - let scaled = values.map { Float(self.topInset) + height - factor * ($0 - self.min.y) } + let scaled = values.map { Double(self.topInset) + height - factor * ($0 - self.min.y) } return scaled } - fileprivate func scaleValueOnYAxis(_ value: Float) -> Float { - - let height = Float(drawingHeight) - var factor: Float + fileprivate func scaleValueOnYAxis(_ value: Double) -> Double { + let height = Double(drawingHeight) + var factor: Double if max.y - min.y == 0 { factor = 0 } else { factor = height / (max.y - min.y) } - let scaled = Float(self.topInset) + height - factor * (value - min.y) + let scaled = Double(self.topInset) + height - factor * (value - min.y) return scaled } - fileprivate func getZeroValueOnYAxis(zeroLevel: Float) -> Float { + fileprivate func getZeroValueOnYAxis(zeroLevel: Double) -> Double { if min.y > zeroLevel { return scaleValueOnYAxis(min.y) } else { return scaleValueOnYAxis(zeroLevel) } - } // MARK: - Drawings - fileprivate func drawLine(_ xValues: [Float], yValues: [Float], seriesIndex: Int) { + fileprivate func drawLine(_ xValues: [Double], yValues: [Double], seriesIndex: Int) { // YValues are "reverted" from top to bottom, so 'above' means <= level let isAboveZeroLine = yValues.max()! <= self.scaleValueOnYAxis(series[seriesIndex].colors.zeroLevel) let path = CGMutablePath() @@ -486,7 +479,7 @@ open class Chart: UIControl { layerStore.append(lineLayer) } - fileprivate func drawArea(_ xValues: [Float], yValues: [Float], seriesIndex: Int) { + fileprivate func drawArea(_ xValues: [Double], yValues: [Double], seriesIndex: Int) { // YValues are "reverted" from top to bottom, so 'above' means <= level let isAboveZeroLine = yValues.max()! <= self.scaleValueOnYAxis(series[seriesIndex].colors.zeroLevel) let area = CGMutablePath() @@ -514,7 +507,6 @@ open class Chart: UIControl { } fileprivate func drawAxes() { - let context = UIGraphicsGetCurrentContext()! context.setStrokeColor(axesColor.cgColor) context.setLineWidth(0.5) @@ -546,19 +538,17 @@ open class Chart: UIControl { context.move(to: CGPoint(x: CGFloat(drawingWidth), y: CGFloat(0))) context.addLine(to: CGPoint(x: CGFloat(drawingWidth), y: drawingHeight + topInset)) context.strokePath() - } fileprivate func drawLabelsAndGridOnXAxis() { - let context = UIGraphicsGetCurrentContext()! context.setStrokeColor(gridColor.cgColor) context.setLineWidth(0.5) - var labels: [Float] + var labels: [Double] if xLabels == nil { // Use labels from the first series - labels = series[0].data.map({ (point: ChartPoint) -> Float in + labels = series[0].data.map({ (point: ChartPoint) -> Double in return point.x }) } else { labels = xLabels! @@ -617,19 +607,16 @@ open class Chart: UIControl { label.frame.origin.x += padding } } - self.addSubview(label) } - } fileprivate func drawLabelsAndGridOnYAxis() { - let context = UIGraphicsGetCurrentContext()! context.setStrokeColor(gridColor.cgColor) context.setLineWidth(0.5) - var labels: [Float] + var labels: [Double] if yLabels == nil { labels = [(min.y + max.y) / 2, max.y] if yLabelsOnRightSide || min.y != 0 { @@ -676,11 +663,8 @@ open class Chart: UIControl { label.frame.origin.y -= label.frame.height self.addSubview(label) - } - UIGraphicsEndImageContext() - } // MARK: - Touch events @@ -710,7 +694,6 @@ open class Chart: UIControl { layer.addSublayer(shapeLayer) layerStore.append(shapeLayer) } - } func handleTouchEvents(_ touches: Set, event: UIEvent!) { @@ -737,7 +720,7 @@ open class Chart: UIControl { for series in self.series { var index: Int? = nil - let xValues = series.data.map({ (point: ChartPoint) -> Float in + let xValues = series.data.map({ (point: ChartPoint) -> Double in return point.x }) let closest = Chart.findClosestInValues(xValues, forValue: x) if closest.lowestIndex != nil && closest.highestIndex != nil { @@ -746,10 +729,9 @@ open class Chart: UIControl { } indexes.append(index) } - delegate!.didTouchChart(self, indexes: indexes, x: x, left: left) - } + override open func touchesBegan(_ touches: Set, with event: UIEvent?) { handleTouchEvents(touches, event: event) } @@ -765,26 +747,26 @@ open class Chart: UIControl { // MARK: - Utilities - fileprivate func valueFromPointAtX(_ x: CGFloat) -> Float { - let value = ((max.x-min.x) / Float(drawingWidth)) * Float(x) + min.x + fileprivate func valueFromPointAtX(_ x: CGFloat) -> Double { + let value = ((max.x-min.x) / Double(drawingWidth)) * Double(x) + min.x return value } - fileprivate func valueFromPointAtY(_ y: CGFloat) -> Float { - let value = ((max.y - min.y) / Float(drawingHeight)) * Float(y) + min.y + fileprivate func valueFromPointAtY(_ y: CGFloat) -> Double { + let value = ((max.y - min.y) / Double(drawingHeight)) * Double(y) + min.y return -value } fileprivate class func findClosestInValues( - _ values: [Float], - forValue value: Float + _ values: [Double], + forValue value: Double ) -> ( - lowestValue: Float?, - highestValue: Float?, + lowestValue: Double?, + highestValue: Double?, lowestIndex: Int?, highestIndex: Int? ) { - var lowestValue: Float?, highestValue: Float?, lowestIndex: Int?, highestIndex: Int? + var lowestValue: Double?, highestValue: Double?, lowestIndex: Int?, highestIndex: Int? values.enumerated().forEach { (i, currentValue) in @@ -810,12 +792,11 @@ open class Chart: UIControl { Segment a line in multiple lines when the line touches the x-axis, i.e. separating positive from negative values. */ - fileprivate class func segmentLine(_ line: ChartLineSegment, zeroLevel: Float) -> [ChartLineSegment] { + fileprivate class func segmentLine(_ line: ChartLineSegment, zeroLevel: Double) -> [ChartLineSegment] { var segments: [ChartLineSegment] = [] var segment: ChartLineSegment = [] line.enumerated().forEach { (i, point) in - segment.append(point) if i < line.count - 1 { let nextPoint = line[i+1] @@ -831,7 +812,6 @@ open class Chart: UIControl { // End of the line segments.append(segment) } - } return segments } @@ -839,9 +819,20 @@ open class Chart: UIControl { /** Return the intersection of a line between two points and 'y = level' line */ - fileprivate class func intersectionWithLevel(_ p1: ChartPoint, and p2: ChartPoint, level: Float) -> ChartPoint { + fileprivate class func intersectionWithLevel(_ p1: ChartPoint, and p2: ChartPoint, level: Double) -> ChartPoint { let dy1 = level - p1.y let dy2 = level - p2.y return (x: (p2.x * dy1 - p1.x * dy2) / (dy1 - dy2), y: level) } } + +extension Sequence where Element == Double { + + func minOrZero() -> Double { + return self.min() ?? 0.0 + } + + func maxOrZero() -> Double { + return self.max() ?? 0.0 + } +} diff --git a/Source/ChartSeries.swift b/Source/ChartSeries.swift index 7b9f6d10e..456f580b0 100644 --- a/Source/ChartSeries.swift +++ b/Source/ChartSeries.swift @@ -11,7 +11,7 @@ import UIKit Represent a series to draw in the line chart. Each series is defined with a dataset and appareance settings. */ open class ChartSeries { - open var data: [(x: Float, y: Float)] + open var data: [(x: Double, y: Double)] open var area: Bool = false open var line: Bool = true open var color: UIColor = ChartColors.blueColor() { @@ -22,23 +22,23 @@ open class ChartSeries { open var colors: ( above: UIColor, below: UIColor, - zeroLevel: Float + zeroLevel: Double ) = (above: ChartColors.blueColor(), below: ChartColors.redColor(), 0) - public init(_ data: [Float]) { + public init(_ data: [Double]) { self.data = [] data.enumerated().forEach { (x, y) in - let point: (x: Float, y: Float) = (x: Float(x), y: y) + let point: (x: Double, y: Double) = (x: Double(x), y: y) self.data.append(point) } } - public init(data: [(x: Float, y: Float)]) { + public init(data: [(x: Double, y: Double)]) { self.data = data } - - public init(data: [(x: Double, y: Double)]) { - self.data = data.map ({ (Float($0.x), Float($0.y))}) + + public init(data: [(x: Float, y: Float)]) { + self.data = data.map { (Double($0.x), Double($0.y)) } } }