diff --git a/Charts/Classes/Charts/BarChartView.swift b/Charts/Classes/Charts/BarChartView.swift index 5501c2fac8..1725482f28 100644 --- a/Charts/Classes/Charts/BarChartView.swift +++ b/Charts/Classes/Charts/BarChartView.swift @@ -23,6 +23,9 @@ public class BarChartView: BarLineChartViewBase, BarChartDataProvider /// if set to true, all values are drawn above their bars, instead of below their top private var _drawValueAboveBarEnabled = true + /// if set to true, all icons are drawn above their bars, instead of below their top + private var _drawIconAboveBarEnabled = true + /// if set to true, a grey area is darawn behind each bar that indicates the maximum value private var _drawBarShadowEnabled = false @@ -147,6 +150,17 @@ public class BarChartView: BarLineChartViewBase, BarChartDataProvider } } + /// if set to true, all icons are drawn above their bars, instead of below their top + public var drawIconAboveBarEnabled: Bool + { + get { return _drawIconAboveBarEnabled; } + set + { + _drawIconAboveBarEnabled = newValue + setNeedsDisplay() + } + } + /// if set to true, a grey area is drawn behind each bar that indicates the maximum value public var drawBarShadowEnabled: Bool { @@ -168,6 +182,8 @@ public class BarChartView: BarLineChartViewBase, BarChartDataProvider /// - returns: true if drawing values above bars is enabled, false if not public var isDrawValueAboveBarEnabled: Bool { return drawValueAboveBarEnabled } + public var isDrawIconAboveBarEnabled: Bool { return drawIconAboveBarEnabled } + /// - returns: true if drawing shadows (maxvalue) for each bar is enabled, false if not public var isDrawBarShadowEnabled: Bool { return drawBarShadowEnabled } } \ No newline at end of file diff --git a/Charts/Classes/Charts/CombinedChartView.swift b/Charts/Classes/Charts/CombinedChartView.swift index 0a1400960d..930e92f79d 100644 --- a/Charts/Classes/Charts/CombinedChartView.swift +++ b/Charts/Classes/Charts/CombinedChartView.swift @@ -195,6 +195,13 @@ public class CombinedChartView: BarLineChartViewBase, LineChartDataProvider, Bar get { return (renderer as! CombinedChartRenderer!).drawValueAboveBarEnabled } set { (renderer as! CombinedChartRenderer!).drawValueAboveBarEnabled = newValue } } + + /// if set to true, all icons are drawn above their bars, instead of below their top + public var drawIconAboveBarEnabled: Bool + { + get { return (renderer as! CombinedChartRenderer!).drawIconAboveBarEnabled } + set { (renderer as! CombinedChartRenderer!).drawIconAboveBarEnabled = newValue } + } /// if set to true, a grey area is darawn behind each bar that indicates the maximum value public var drawBarShadowEnabled: Bool @@ -209,6 +216,9 @@ public class CombinedChartView: BarLineChartViewBase, LineChartDataProvider, Bar /// - returns: true if drawing values above bars is enabled, false if not public var isDrawValueAboveBarEnabled: Bool { return (renderer as! CombinedChartRenderer!).drawValueAboveBarEnabled; } + /// - returns: true if drawing icons above bars is enabled, false if not + public var isDrawIconAboveBarEnabled: Bool { return (renderer as! CombinedChartRenderer!).drawIconAboveBarEnabled; } + /// - returns: true if drawing shadows (maxvalue) for each bar is enabled, false if not public var isDrawBarShadowEnabled: Bool { return (renderer as! CombinedChartRenderer!).drawBarShadowEnabled; } diff --git a/Charts/Classes/Data/ChartDataSet.swift b/Charts/Classes/Data/ChartDataSet.swift index c9bdf5e69c..3abd81d1e6 100644 --- a/Charts/Classes/Data/ChartDataSet.swift +++ b/Charts/Classes/Data/ChartDataSet.swift @@ -32,6 +32,7 @@ public class ChartDataSet: NSObject public var label: String? = "DataSet" public var visible = true public var drawValuesEnabled = true + public var drawIconsEnabled = true /// the color used for the value-text public var valueTextColor: UIColor = UIColor.blackColor() @@ -505,6 +506,11 @@ public class ChartDataSet: NSObject return drawValuesEnabled } + public var isDrawIconsEnabled: Bool + { + return drawIconsEnabled + } + /// Checks if this DataSet contains the specified Entry. /// - returns: true if contains the entry, false if not. public func contains(e: ChartDataEntry) -> Bool diff --git a/Charts/Classes/Interfaces/BarChartDataProvider.swift b/Charts/Classes/Interfaces/BarChartDataProvider.swift index e1482c6269..5dab73db05 100644 --- a/Charts/Classes/Interfaces/BarChartDataProvider.swift +++ b/Charts/Classes/Interfaces/BarChartDataProvider.swift @@ -21,5 +21,6 @@ public protocol BarChartDataProvider: BarLineScatterCandleBubbleChartDataProvide var isDrawBarShadowEnabled: Bool { get } var isDrawValueAboveBarEnabled: Bool { get } + var isDrawIconAboveBarEnabled: Bool { get } var isDrawHighlightArrowEnabled: Bool { get } } \ No newline at end of file diff --git a/Charts/Classes/Renderers/BarChartRenderer.swift b/Charts/Classes/Renderers/BarChartRenderer.swift index af3f3bf723..9824f6ae7e 100644 --- a/Charts/Classes/Renderers/BarChartRenderer.swift +++ b/Charts/Classes/Renderers/BarChartRenderer.swift @@ -425,9 +425,196 @@ public class BarChartRenderer: ChartDataRendererBase ChartUtils.drawText(context: context, text: value, point: CGPoint(x: xPos, y: yPos), align: align, attributes: [NSFontAttributeName: font, NSForegroundColorAttributeName: color]) } + internal func drawImage(context context: CGContext, image: UIImage, point: CGPoint, expectedSize: CGSize) + { + ChartUtils.drawImage(context: context, image: image, point: point, expectedSize: expectedSize) + } + public override func drawExtras(context context: CGContext) { - + // if values are drawn + if (passesCheck()) + { + guard let dataProvider = dataProvider, barData = dataProvider.barData else { return } + + var dataSets = barData.dataSets + + let drawValueAboveBar = dataProvider.isDrawIconAboveBarEnabled + + var posOffset: CGFloat + var negOffset: CGFloat + + let imageProportion: CGFloat = 0.8 + let dataSetCountScalar: CGFloat = barData.dataSetCount > 1 ? CGFloat(barData.dataSetCount) + 1.0 : CGFloat(barData.dataSetCount) + + for (var i = 0, count = barData.dataSetCount; i < count; i++) + { + let dataSet = dataSets[i] as! BarChartDataSet + let barSpace = dataSet.barSpace + + if !dataSet.isDrawIconsEnabled || dataSet.entryCount == 0 + { + continue + } + + + let trans = dataProvider.getTransformer(dataSet.axisDependency) + + var entries = dataSet.yVals as! [BarChartDataEntry] + + var valuePoints = getTransformedValues(trans: trans, entries: entries, dataSetIndex: i) + + // calculate the image size + var imageDimension: CGFloat = 0 + if valuePoints.count > 1 { + imageDimension = imageProportion * (1 - barSpace) * (valuePoints[1].x - valuePoints[0].x) + } + else if valuePoints.count == 1 { + imageDimension = imageProportion * (1 - barSpace) * (valuePoints[0].x) + } + + imageDimension = imageDimension / dataSetCountScalar + + + // calculate the correct offset depending on the draw position of the value + let valueOffsetPlus: CGFloat = 2.5 + imageDimension / 2 + posOffset = (drawValueAboveBar ? -(valueOffsetPlus) : valueOffsetPlus) + negOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueOffsetPlus)) + + // if only single values are drawn (sum) + if (!dataSet.isStacked) + { + for (var j = 0, count = Int(ceil(CGFloat(valuePoints.count) * _animator.phaseX)); j < count; j++) + { + if (!viewPortHandler.isInBoundsRight(valuePoints[j].x)) + { + break + } + + if (!viewPortHandler.isInBoundsY(valuePoints[j].y) + || !viewPortHandler.isInBoundsLeft(valuePoints[j].x)) + { + continue + } + + if let imageName = entries[j].data as! String! { + if let image = UIImage(named: imageName) { + let expectedSize = CGSizeMake(imageDimension, imageDimension) + + drawImage(context: context, + image: image, + point: CGPoint( + x: valuePoints[j].x, + y: valuePoints[j].y + (entries[j].value >= 0.0 ? posOffset : negOffset) + ), + expectedSize: expectedSize) + } + } + } + } + else + { + // if we have stacks + for (var j = 0, count = Int(ceil(CGFloat(valuePoints.count) * _animator.phaseX)); j < count; j++) + { + let e = entries[j] + + let values = e.values + + // we still draw stacked bars, but there is one non-stacked in between + if (values == nil) + { + if (!viewPortHandler.isInBoundsRight(valuePoints[j].x)) + { + break + } + + if (!viewPortHandler.isInBoundsY(valuePoints[j].y) + || !viewPortHandler.isInBoundsLeft(valuePoints[j].x)) + { + continue + } + + if let imageName = entries[j].data as! String! { + if let image = UIImage(named: imageName) { + let expectedSize = CGSizeMake(imageDimension, imageDimension) + + drawImage(context: context, + image: image, + point: CGPoint( + x: valuePoints[j].x, + y: valuePoints[j].y + ), + expectedSize: expectedSize) + } + } + } + else + { + // draw stack values + let vals = values! + var transformed = [CGPoint]() + + var posY = 0.0 + var negY = -e.negativeSum + + for (var k = 0; k < vals.count; k++) + { + let value = vals[k] + var y: Double + + if value >= 0.0 + { + posY += value + y = posY + } + else + { + y = negY + negY -= value + } + + transformed.append(CGPoint(x: 0.0, y: CGFloat(y) * _animator.phaseY)) + } + + trans.pointValuesToPixel(&transformed) + + for (var k = 0; k < transformed.count; k++) + { + let x = valuePoints[j].x + let y = transformed[k].y + (vals[k] >= 0 ? posOffset : negOffset) + + if (!viewPortHandler.isInBoundsRight(x)) + { + break + } + + if (!viewPortHandler.isInBoundsY(y) || !viewPortHandler.isInBoundsLeft(x)) + { + continue + } + + if let dataArray = entries[j].data as! [String]! { + if let imageName = dataArray[k] as String! { + if let image = UIImage(named: imageName) { + let expectedSize = CGSizeMake(imageDimension, imageDimension) + + drawImage(context: context, + image: image, + point: CGPoint( + x: x, + y: y + ), + expectedSize: expectedSize) + } + } + } + } + } + } + } + } + } } private var _highlightArrowPtsBuffer = [CGPoint](count: 3, repeatedValue: CGPoint()) diff --git a/Charts/Classes/Renderers/CombinedChartRenderer.swift b/Charts/Classes/Renderers/CombinedChartRenderer.swift index 337d249a57..43395657e7 100644 --- a/Charts/Classes/Renderers/CombinedChartRenderer.swift +++ b/Charts/Classes/Renderers/CombinedChartRenderer.swift @@ -24,6 +24,9 @@ public class CombinedChartRenderer: ChartDataRendererBase /// if set to true, all values are drawn above their bars, instead of below their top public var drawValueAboveBarEnabled = true + /// if set to true, all icons are drawn above their bars, instead of below their top + public var drawIconAboveBarEnabled = true + /// if set to true, a grey area is darawn behind each bar that indicates the maximum value public var drawBarShadowEnabled = true diff --git a/Charts/Classes/Utils/ChartUtils.swift b/Charts/Classes/Utils/ChartUtils.swift index 63b74b14c2..89b49862bc 100755 --- a/Charts/Classes/Utils/ChartUtils.swift +++ b/Charts/Classes/Utils/ChartUtils.swift @@ -244,6 +244,30 @@ public class ChartUtils drawMultilineText(context: context, text: text, knownTextSize: rect.size, point: point, attributes: attributes, constrainedToSize: constrainedToSize, anchor: anchor, angleRadians: angleRadians) } + public class func drawImage(context context: CGContext, image: UIImage, point: CGPoint, expectedSize: CGSize) + { + var drawOffset = CGPoint() + drawOffset.x += point.x + drawOffset.x -= expectedSize.width / 2 + drawOffset.y += point.y + drawOffset.y -= expectedSize.height / 2 + + UIGraphicsPushContext(context) + + if image.size.width != expectedSize.width && image.size.height != expectedSize.height { + UIGraphicsBeginImageContextWithOptions(expectedSize, false, 0.0) + image.drawInRect(CGRect(origin: CGPointZero, size: expectedSize)) + let scaledImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + scaledImage.drawAtPoint(drawOffset) + } + else { + image.drawAtPoint(drawOffset) + } + + UIGraphicsPopContext() + } + /// - returns: an angle between 0.0 < 360.0 (not less than zero, less than 360) internal class func normalizedAngleFromAngle(var angle: CGFloat) -> CGFloat { diff --git a/ChartsDemo/Classes/Demos/BarChartViewController.m b/ChartsDemo/Classes/Demos/BarChartViewController.m index c3208c76a9..4fa5930ff8 100644 --- a/ChartsDemo/Classes/Demos/BarChartViewController.m +++ b/ChartsDemo/Classes/Demos/BarChartViewController.m @@ -112,7 +112,9 @@ - (void)setDataCount:(int)count range:(double)range { double mult = (range + 1); double val = (double) (arc4random_uniform(mult)); - [yVals addObject:[[BarChartDataEntry alloc] initWithValue:val xIndex:i]]; + BarChartDataEntry *entry = [[BarChartDataEntry alloc] initWithValue:val xIndex:i]; + entry.data = @"Icon-29@2x.png"; + [yVals addObject: entry]; } BarChartDataSet *set1 = [[BarChartDataSet alloc] initWithYVals:yVals label:@"DataSet"]; diff --git a/ChartsDemo/Classes/Demos/MultipleBarChartViewController.m b/ChartsDemo/Classes/Demos/MultipleBarChartViewController.m index 0d4224a812..3426fddb8d 100644 --- a/ChartsDemo/Classes/Demos/MultipleBarChartViewController.m +++ b/ChartsDemo/Classes/Demos/MultipleBarChartViewController.m @@ -103,13 +103,19 @@ - (void)setDataCount:(int)count range:(double)range for (int i = 0; i < count; i++) { double val = (double) (arc4random_uniform(mult) + 3.0); - [yVals1 addObject:[[BarChartDataEntry alloc] initWithValue:val xIndex:i]]; + BarChartDataEntry *entry = [[BarChartDataEntry alloc] initWithValue:val xIndex:i]; + entry.data = @"Icon-29@2x.png"; + [yVals1 addObject: entry]; val = (double) (arc4random_uniform(mult) + 3.0); - [yVals2 addObject:[[BarChartDataEntry alloc] initWithValue:val xIndex:i]]; + BarChartDataEntry *entry2 = [[BarChartDataEntry alloc] initWithValue:val xIndex:i]; + entry2.data = @"Icon-29@2x.png"; + [yVals2 addObject: entry2]; val = (double) (arc4random_uniform(mult) + 3.0); - [yVals3 addObject:[[BarChartDataEntry alloc] initWithValue:val xIndex:i]]; + BarChartDataEntry *entry3 = [[BarChartDataEntry alloc] initWithValue:val xIndex:i]; + entry3.data = @"Icon-29@2x.png"; + [yVals3 addObject: entry3]; } BarChartDataSet *set1 = [[BarChartDataSet alloc] initWithYVals:yVals1 label:@"Company A"]; diff --git a/ChartsDemo/Classes/Demos/StackedBarChartViewController.m b/ChartsDemo/Classes/Demos/StackedBarChartViewController.m index 580577dd08..6e4f522135 100644 --- a/ChartsDemo/Classes/Demos/StackedBarChartViewController.m +++ b/ChartsDemo/Classes/Demos/StackedBarChartViewController.m @@ -103,7 +103,9 @@ - (void)setDataCount:(int)count range:(double)range double val2 = (double) (arc4random_uniform(mult) + mult / 3); double val3 = (double) (arc4random_uniform(mult) + mult / 3); - [yVals addObject:[[BarChartDataEntry alloc] initWithValues:@[@(val1), @(val2), @(val3)] xIndex:i]]; + BarChartDataEntry *entry = [[BarChartDataEntry alloc] initWithValues:@[@(val1), @(val2), @(val3)] xIndex:i]; + entry.data = [NSArray arrayWithObjects: @"Icon-29@2x.png", @"Icon-29@2x.png", @"Icon-29@2x.png", nil]; + [yVals addObject: entry]; } BarChartDataSet *set1 = [[BarChartDataSet alloc] initWithYVals:yVals label:@"Statistics Vienna 2014"];