diff --git a/Source/Charts/Data/Implementations/ChartBaseDataSet.swift b/Source/Charts/Data/Implementations/ChartBaseDataSet.swift index c8515aa7c5..8a72cbf58e 100644 --- a/Source/Charts/Data/Implementations/ChartBaseDataSet.swift +++ b/Source/Charts/Data/Implementations/ChartBaseDataSet.swift @@ -302,6 +302,9 @@ open class ChartBaseDataSet: NSObject, ChartDataSetProtocol /// the font for the value-text labels open var valueFont: NSUIFont = NSUIFont.systemFont(ofSize: 7.0) + /// The rotation angle (in degrees) for value-text labels + open var valueLabelAngle: CGFloat = CGFloat(0.0) + /// The form to draw for this dataset in the legend. open var form = Legend.Form.default diff --git a/Source/Charts/Data/Interfaces/ChartDataSetProtocol.swift b/Source/Charts/Data/Interfaces/ChartDataSetProtocol.swift index ee017d786a..0bf371d5bf 100644 --- a/Source/Charts/Data/Interfaces/ChartDataSetProtocol.swift +++ b/Source/Charts/Data/Interfaces/ChartDataSetProtocol.swift @@ -200,6 +200,9 @@ public protocol ChartDataSetProtocol /// the font for the value-text labels var valueFont: NSUIFont { get set } + /// The rotation angle (in degrees) for value-text labels + var valueLabelAngle: CGFloat { get set } + /// The form to draw for this dataset in the legend. /// /// Return `.Default` to use the default legend form. diff --git a/Source/Charts/Renderers/BarChartRenderer.swift b/Source/Charts/Renderers/BarChartRenderer.swift index 27d4050b5e..6e37d3445b 100644 --- a/Source/Charts/Renderers/BarChartRenderer.swift +++ b/Source/Charts/Renderers/BarChartRenderer.swift @@ -395,6 +395,8 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer shouldDrawValues(forDataSet: dataSet) else { continue } + let angleRadians = dataSet.valueLabelAngle.DEG2RAD + let isInverted = dataProvider.isInverted(axis: dataSet.axisDependency) // calculate the correct offset depending on the draw position of the value @@ -454,7 +456,9 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer : (rect.origin.y + rect.size.height + negOffset), font: valueFont, align: .center, - color: dataSet.valueTextColorAt(j)) + color: dataSet.valueTextColorAt(j), + anchor: CGPoint(x: 0.5, y: 0.5), + angleRadians: angleRadians) } if let icon = e.icon, dataSet.isDrawIconsEnabled @@ -471,6 +475,7 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer atCenter: CGPoint(x: px, y: py), size: icon.size) } + } } else @@ -547,7 +552,9 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer yPos: y, font: valueFont, align: .center, - color: dataSet.valueTextColorAt(index)) + color: dataSet.valueTextColorAt(index), + anchor: CGPoint(x: 0.5, y: 0.5), + angleRadians: angleRadians) } if let icon = e.icon, dataSet.isDrawIconsEnabled @@ -579,7 +586,9 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer (e.y >= 0 ? posOffset : negOffset), font: valueFont, align: .center, - color: dataSet.valueTextColorAt(index)) + color: dataSet.valueTextColorAt(index), + anchor: CGPoint(x: 0.5, y: 0.5), + angleRadians: angleRadians) } if let icon = e.icon, dataSet.isDrawIconsEnabled @@ -605,10 +614,19 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer } /// Draws a value at the specified x and y position. - @objc open func drawValue(context: CGContext, value: String, xPos: CGFloat, yPos: CGFloat, font: NSUIFont, align: NSTextAlignment, color: NSUIColor) + @objc open func drawValue(context: CGContext, value: String, xPos: CGFloat, yPos: CGFloat, font: NSUIFont, align: NSTextAlignment, color: NSUIColor, anchor: CGPoint, angleRadians: CGFloat) { - context.drawText(value, at: CGPoint(x: xPos, y: yPos), align: align, attributes: [.font: font, .foregroundColor: color]) + if (angleRadians == 0.0) + { + context.drawText(value, at: CGPoint(x: xPos, y: yPos), align: align, attributes: [.font: font, .foregroundColor: color]) + } + else + { + // align left to center text with rotation + context.drawText(value, at: CGPoint(x: xPos, y: yPos), align: align, anchor: anchor, angleRadians: angleRadians, attributes: [.font: font, .foregroundColor: color]) + } } + open override func drawExtras(context: CGContext) { diff --git a/Source/Charts/Renderers/BubbleChartRenderer.swift b/Source/Charts/Renderers/BubbleChartRenderer.swift index 16fecd6521..36303c8d71 100644 --- a/Source/Charts/Renderers/BubbleChartRenderer.swift +++ b/Source/Charts/Renderers/BubbleChartRenderer.swift @@ -182,6 +182,8 @@ open class BubbleChartRenderer: BarLineScatterCandleBubbleRenderer let valueToPixelMatrix = trans.valueToPixelMatrix let iconsOffset = dataSet.iconsOffset + + let angleRadians = dataSet.valueLabelAngle.DEG2RAD for j in _xBounds.min..._xBounds.range + _xBounds.min { @@ -216,6 +218,7 @@ open class BubbleChartRenderer: BarLineScatterCandleBubbleRenderer at: CGPoint(x: pt.x, y: pt.y - (0.5 * lineHeight)), align: .center, + angleRadians: angleRadians, attributes: [.font: valueFont, .foregroundColor: valueTextColor]) } diff --git a/Source/Charts/Renderers/CandleStickChartRenderer.swift b/Source/Charts/Renderers/CandleStickChartRenderer.swift index 8194b5dcf2..31e1e0278f 100644 --- a/Source/Charts/Renderers/CandleStickChartRenderer.swift +++ b/Source/Charts/Renderers/CandleStickChartRenderer.swift @@ -309,6 +309,8 @@ open class CandleStickChartRenderer: LineScatterCandleRadarRenderer let iconsOffset = dataSet.iconsOffset + let angleRadians = dataSet.valueLabelAngle.DEG2RAD + _xBounds.set(chart: dataProvider, dataSet: dataSet, animator: animator) let lineHeight = valueFont.lineHeight @@ -341,6 +343,7 @@ open class CandleStickChartRenderer: LineScatterCandleRadarRenderer at: CGPoint(x: pt.x, y: pt.y - yOffset), align: .center, + angleRadians: angleRadians, attributes: [.font: valueFont, .foregroundColor: dataSet.valueTextColorAt(j)]) } diff --git a/Source/Charts/Renderers/HorizontalBarChartRenderer.swift b/Source/Charts/Renderers/HorizontalBarChartRenderer.swift index ae7d761e1d..ae27a4a4ef 100644 --- a/Source/Charts/Renderers/HorizontalBarChartRenderer.swift +++ b/Source/Charts/Renderers/HorizontalBarChartRenderer.swift @@ -337,6 +337,8 @@ open class HorizontalBarChartRenderer: BarChartRenderer { guard let dataSet = dataSets[dataSetIndex] as? BarChartDataSetProtocol else { continue } + let angleRadians = dataSet.valueLabelAngle.DEG2RAD + if !shouldDrawValues(forDataSet: dataSet) || !(dataSet.isDrawIconsEnabled && dataSet.isVisible) { continue @@ -411,7 +413,9 @@ open class HorizontalBarChartRenderer: BarChartRenderer yPos: y + yOffset, font: valueFont, align: textAlign, - color: dataSet.valueTextColorAt(j)) + color: dataSet.valueTextColorAt(j), + anchor: CGPoint.zero, + angleRadians: angleRadians) } if let icon = e.icon, dataSet.isDrawIconsEnabled @@ -489,7 +493,9 @@ open class HorizontalBarChartRenderer: BarChartRenderer yPos: rect.origin.y + yOffset, font: valueFont, align: textAlign, - color: dataSet.valueTextColorAt(index)) + color: dataSet.valueTextColorAt(index), + anchor: CGPoint.zero, + angleRadians: angleRadians) } if let icon = e.icon, dataSet.isDrawIconsEnabled @@ -588,7 +594,9 @@ open class HorizontalBarChartRenderer: BarChartRenderer yPos: y + yOffset, font: valueFont, align: textAlign, - color: dataSet.valueTextColorAt(index)) + color: dataSet.valueTextColorAt(index), + anchor: CGPoint.zero, + angleRadians: angleRadians) } if let icon = e.icon, dataSet.isDrawIconsEnabled diff --git a/Source/Charts/Renderers/LineChartRenderer.swift b/Source/Charts/Renderers/LineChartRenderer.swift index ed5edcb77a..85b9337b82 100644 --- a/Source/Charts/Renderers/LineChartRenderer.swift +++ b/Source/Charts/Renderers/LineChartRenderer.swift @@ -546,6 +546,8 @@ open class LineChartRenderer: LineRadarRenderer let formatter = dataSet.valueFormatter + let angleRadians = dataSet.valueLabelAngle.DEG2RAD + let trans = dataProvider.getTransformer(forAxis: dataSet.axisDependency) let valueToPixelMatrix = trans.valueToPixelMatrix @@ -587,7 +589,7 @@ open class LineChartRenderer: LineRadarRenderer viewPortHandler: viewPortHandler), at: CGPoint(x: pt.x, y: pt.y - CGFloat(valOffset) - valueFont.lineHeight), - align: .center, + angleRadians: angleRadians, attributes: [.font: valueFont, .foregroundColor: dataSet.valueTextColorAt(j)]) } diff --git a/Source/Charts/Renderers/PieChartRenderer.swift b/Source/Charts/Renderers/PieChartRenderer.swift index e2d9e7d57f..c2d226966f 100644 --- a/Source/Charts/Renderers/PieChartRenderer.swift +++ b/Source/Charts/Renderers/PieChartRenderer.swift @@ -350,6 +350,8 @@ open class PieChartRenderer: NSObject, DataRenderer let iconsOffset = dataSet.iconsOffset + let angleRadians = dataSet.valueLabelAngle.DEG2RAD + let xValuePosition = dataSet.xValuePosition let yValuePosition = dataSet.yValuePosition @@ -465,6 +467,7 @@ open class PieChartRenderer: NSObject, DataRenderer context.drawText(valueText, at: labelPoint, align: align, + angleRadians: angleRadians, attributes: [.font: valueFont, .foregroundColor: valueTextColor]) @@ -474,6 +477,7 @@ open class PieChartRenderer: NSObject, DataRenderer at: CGPoint(x: labelPoint.x, y: labelPoint.y + lineHeight), align: align, + angleRadians: angleRadians, attributes: [.font: entryLabelFont ?? valueFont, .foregroundColor: entryLabelColor ?? valueTextColor]) } @@ -486,6 +490,7 @@ open class PieChartRenderer: NSObject, DataRenderer at: CGPoint(x: labelPoint.x, y: labelPoint.y + lineHeight / 2.0), align: align, + angleRadians: angleRadians, attributes: [.font: entryLabelFont ?? valueFont, .foregroundColor: entryLabelColor ?? valueTextColor]) } @@ -496,6 +501,7 @@ open class PieChartRenderer: NSObject, DataRenderer at: CGPoint(x: labelPoint.x, y: labelPoint.y + lineHeight / 2.0), align: align, + angleRadians: angleRadians, attributes: [.font: valueFont, .foregroundColor: valueTextColor]) } @@ -512,6 +518,7 @@ open class PieChartRenderer: NSObject, DataRenderer context.drawText(valueText, at: CGPoint(x: x, y: y), align: .center, + angleRadians: angleRadians, attributes: [.font: valueFont, .foregroundColor: valueTextColor]) if j < data.entryCount && pe?.label != nil @@ -519,6 +526,7 @@ open class PieChartRenderer: NSObject, DataRenderer context.drawText(pe!.label!, at: CGPoint(x: x, y: y + lineHeight), align: .center, + angleRadians: angleRadians, attributes: [.font: entryLabelFont ?? valueFont, .foregroundColor: entryLabelColor ?? valueTextColor]) } @@ -530,6 +538,7 @@ open class PieChartRenderer: NSObject, DataRenderer context.drawText(pe!.label!, at: CGPoint(x: x, y: y + lineHeight / 2.0), align: .center, + angleRadians: angleRadians, attributes: [.font: entryLabelFont ?? valueFont, .foregroundColor: entryLabelColor ?? valueTextColor]) } @@ -539,6 +548,7 @@ open class PieChartRenderer: NSObject, DataRenderer context.drawText(valueText, at: CGPoint(x: x, y: y + lineHeight / 2.0), align: .center, + angleRadians: angleRadians, attributes: [.font: valueFont, .foregroundColor: valueTextColor]) } } diff --git a/Source/Charts/Renderers/RadarChartRenderer.swift b/Source/Charts/Renderers/RadarChartRenderer.swift index 092a1e10b9..77b605a872 100644 --- a/Source/Charts/Renderers/RadarChartRenderer.swift +++ b/Source/Charts/Renderers/RadarChartRenderer.swift @@ -232,6 +232,8 @@ open class RadarChartRenderer: LineRadarRenderer continue } + let angleRadians = dataSet.valueLabelAngle.DEG2RAD + let entryCount = dataSet.entryCount let iconsOffset = dataSet.iconsOffset @@ -255,6 +257,7 @@ open class RadarChartRenderer: LineRadarRenderer viewPortHandler: viewPortHandler), at: CGPoint(x: p.x, y: p.y - yoffset - valueFont.lineHeight), align: .center, + angleRadians: angleRadians, attributes: [.font: valueFont, .foregroundColor: dataSet.valueTextColorAt(j)]) } diff --git a/Source/Charts/Renderers/ScatterChartRenderer.swift b/Source/Charts/Renderers/ScatterChartRenderer.swift index eec555598a..e433f16af0 100644 --- a/Source/Charts/Renderers/ScatterChartRenderer.swift +++ b/Source/Charts/Renderers/ScatterChartRenderer.swift @@ -143,6 +143,8 @@ open class ScatterChartRenderer: LineScatterCandleRadarRenderer let iconsOffset = dataSet.iconsOffset + let angleRadians = dataSet.valueLabelAngle.DEG2RAD + let shapeSize = dataSet.scatterShapeSize let lineHeight = valueFont.lineHeight @@ -180,6 +182,7 @@ open class ScatterChartRenderer: LineScatterCandleRadarRenderer at: CGPoint(x: pt.x, y: pt.y - shapeSize - lineHeight), align: .center, + angleRadians: angleRadians, attributes: [.font: valueFont, .foregroundColor: dataSet.valueTextColorAt(j)] ) diff --git a/Source/Charts/Utils/ChartUtils.swift b/Source/Charts/Utils/ChartUtils.swift index 6636bde522..391098e82b 100644 --- a/Source/Charts/Utils/ChartUtils.swift +++ b/Source/Charts/Utils/ChartUtils.swift @@ -149,27 +149,25 @@ extension CGContext { NSUIGraphicsPopContext() } - open func drawText(_ text: String, at point: CGPoint, align: NSTextAlignment, attributes: [NSAttributedStringKey : Any]?) + open func drawText(_ text: String, at point: CGPoint, align: NSTextAlignment, anchor: CGPoint = CGPoint(x: 0.5, y: 0.5), angleRadians: CGFloat = 0.0, attributes: [NSAttributedStringKey : Any]?) { - var point = point - - if align == .center + let drawPoint = getDrawPoint(text: text, point: point, align: align, attributes: attributes) + + if (angleRadians == 0.0) { - point.x -= text.size(withAttributes: attributes).width / 2.0 + NSUIGraphicsPushContext(self) + + (text as NSString).draw(at: drawPoint, withAttributes: attributes) + + NSUIGraphicsPopContext() } - else if align == .right + else { - point.x -= text.size(withAttributes: attributes).width + drawText(text, at: drawPoint, anchor: anchor, angleRadians: angleRadians, attributes: attributes) } - - NSUIGraphicsPushContext(self) - - (text as NSString).draw(at: point, withAttributes: attributes) - - NSUIGraphicsPopContext() } - - open func drawText(_ text: String, at point: CGPoint, anchor: CGPoint, angleRadians: CGFloat, attributes: [NSAttributedStringKey : Any]?) + + open func drawText(_ text: String, at point: CGPoint, anchor: CGPoint = CGPoint(x: 0.5, y: 0.5), angleRadians: CGFloat, attributes: [NSAttributedStringKey : Any]?) { var drawOffset = CGPoint() @@ -221,6 +219,21 @@ extension CGContext { NSUIGraphicsPopContext() } + private func getDrawPoint(text: String, point: CGPoint, align: NSTextAlignment, attributes: [NSAttributedStringKey : Any]?) -> CGPoint + { + var point = point + + if align == .center + { + point.x -= text.size(withAttributes: attributes).width / 2.0 + } + else if align == .right + { + point.x -= text.size(withAttributes: attributes).width + } + return point + } + func drawMultilineText(_ text: String, at point: CGPoint, constrainedTo size: CGSize, anchor: CGPoint, knownTextSize: CGSize, angleRadians: CGFloat, attributes: [NSAttributedStringKey : Any]?) { var rect = CGRect(origin: .zero, size: knownTextSize)